From e9eb7cc3c34407440da5442e5cc0a0b7e43ffc8a Mon Sep 17 00:00:00 2001 From: Cuynu Date: Fri, 16 Aug 2024 05:22:42 +0700 Subject: [PATCH] Import SocksDroid with my changes --- .gitignore | 14 + .idea/codeStyles/Project.xml | 113 + .idea/compiler.xml | 6 + .idea/deploymentTargetDropDown.xml | 17 + .idea/deploymentTargetSelector.xml | 10 + .idea/gradle.xml | 20 + .idea/inspectionProfiles/Project_Default.xml | 10 + .idea/jarRepositories.xml | 25 + .idea/migrations.xml | 10 + .idea/misc.xml | 22 + .idea/vcs.xml | 6 + LICENSE | 674 + README.md | 12 + app/.gitignore | 1 + app/build.gradle | 31 + app/proguard-rules.pro | 17 + app/src/main/.gitignore | 1 + app/src/main/AndroidManifest.xml | 47 + .../aidl/net/typeblog/socks/IVpnService.aidl | 7 + app/src/main/build-jni.sh | 12 + app/src/main/ic_launcher-web.png | Bin 0 -> 6179 bytes .../java/net/typeblog/socks/BootReceiver.java | 32 + .../java/net/typeblog/socks/MainActivity.java | 13 + .../net/typeblog/socks/ProfileFragment.java | 416 + .../net/typeblog/socks/SocksVpnService.java | 275 + .../main/java/net/typeblog/socks/System.java | 11 + .../net/typeblog/socks/util/Constants.java | 41 + .../java/net/typeblog/socks/util/Profile.java | 169 + .../typeblog/socks/util/ProfileManager.java | 87 + .../java/net/typeblog/socks/util/Routes.java | 27 + .../java/net/typeblog/socks/util/Utility.java | 141 + app/src/main/jni/Android.mk | 159 + app/src/main/jni/Application.mk | 3 + app/src/main/jni/badvpn/CMakeLists.txt | 394 + app/src/main/jni/badvpn/COPYING | 24 + app/src/main/jni/badvpn/ChangeLog | 192 + app/src/main/jni/badvpn/INSTALL | 76 + app/src/main/jni/badvpn/INSTALL-WINDOWS | 72 + app/src/main/jni/badvpn/arpprobe/BArpProbe.c | 359 + app/src/main/jni/badvpn/arpprobe/BArpProbe.h | 80 + .../main/jni/badvpn/arpprobe/CMakeLists.txt | 2 + app/src/main/jni/badvpn/badvpn.7 | 324 + app/src/main/jni/badvpn/base/BLog.c | 88 + app/src/main/jni/badvpn/base/BLog.h | 399 + app/src/main/jni/badvpn/base/BLog_syslog.c | 150 + app/src/main/jni/badvpn/base/BLog_syslog.h | 42 + app/src/main/jni/badvpn/base/BMutex.h | 101 + app/src/main/jni/badvpn/base/BPending.c | 205 + app/src/main/jni/badvpn/base/BPending.h | 250 + app/src/main/jni/badvpn/base/BPending_list.h | 4 + app/src/main/jni/badvpn/base/CMakeLists.txt | 13 + app/src/main/jni/badvpn/base/DebugObject.c | 39 + app/src/main/jni/badvpn/base/DebugObject.h | 147 + app/src/main/jni/badvpn/blog_channels.txt | 145 + .../main/jni/badvpn/blog_generator/blog.php | 121 + .../badvpn/blog_generator/blog_functions.php | 35 + app/src/main/jni/badvpn/bproto/BProto.h | 85 + .../badvpn/bproto_generator/ProtoParser.lime | 99 + .../badvpn/bproto_generator/ProtoParser.php | 560 + .../jni/badvpn/bproto_generator/bproto.php | 115 + .../bproto_generator/bproto_functions.php | 777 ++ app/src/main/jni/badvpn/client/CMakeLists.txt | 30 + app/src/main/jni/badvpn/client/DPReceive.c | 324 + app/src/main/jni/badvpn/client/DPReceive.h | 98 + app/src/main/jni/badvpn/client/DPRelay.c | 307 + app/src/main/jni/badvpn/client/DPRelay.h | 89 + app/src/main/jni/badvpn/client/DataProto.c | 566 + app/src/main/jni/badvpn/client/DataProto.h | 237 + .../badvpn/client/DataProtoKeepaliveSource.c | 72 + .../badvpn/client/DataProtoKeepaliveSource.h | 73 + .../main/jni/badvpn/client/DatagramPeerIO.c | 425 + .../main/jni/badvpn/client/DatagramPeerIO.h | 271 + .../badvpn/client/FragmentProtoAssembler.c | 469 + .../badvpn/client/FragmentProtoAssembler.h | 134 + .../client/FragmentProtoAssembler_tree.h | 9 + .../badvpn/client/FragmentProtoDisassembler.c | 229 + .../badvpn/client/FragmentProtoDisassembler.h | 109 + app/src/main/jni/badvpn/client/FrameDecider.c | 795 ++ app/src/main/jni/badvpn/client/FrameDecider.h | 196 + .../badvpn/client/FrameDecider_groups_tree.h | 9 + .../badvpn/client/FrameDecider_macs_tree.h | 9 + .../client/FrameDecider_multicast_tree.h | 9 + .../main/jni/badvpn/client/PasswordListener.c | 374 + .../main/jni/badvpn/client/PasswordListener.h | 156 + app/src/main/jni/badvpn/client/PeerChat.c | 433 + app/src/main/jni/badvpn/client/PeerChat.h | 123 + .../main/jni/badvpn/client/SCOutmsgEncoder.c | 104 + .../main/jni/badvpn/client/SCOutmsgEncoder.h | 76 + .../main/jni/badvpn/client/SPProtoDecoder.c | 398 + .../main/jni/badvpn/client/SPProtoDecoder.h | 171 + .../main/jni/badvpn/client/SPProtoEncoder.c | 436 + .../main/jni/badvpn/client/SPProtoEncoder.h | 172 + .../jni/badvpn/client/SimpleStreamBuffer.c | 144 + .../jni/badvpn/client/SimpleStreamBuffer.h | 52 + .../jni/badvpn/client/SinglePacketSource.c | 85 + .../jni/badvpn/client/SinglePacketSource.h | 73 + app/src/main/jni/badvpn/client/StreamPeerIO.c | 712 + app/src/main/jni/badvpn/client/StreamPeerIO.h | 222 + .../main/jni/badvpn/client/badvpn-client.8 | 316 + app/src/main/jni/badvpn/client/client.c | 2997 ++++ app/src/main/jni/badvpn/client/client.h | 193 + .../cmake/modules/COPYING-CMAKE-SCRIPTS | 22 + .../jni/badvpn/cmake/modules/FindGLIB2.cmake | 52 + .../cmake/modules/FindLibraryWithDebug.cmake | 113 + .../jni/badvpn/cmake/modules/FindNSPR.cmake | 57 + .../jni/badvpn/cmake/modules/FindNSS.cmake | 57 + .../badvpn/cmake/modules/FindOpenSSL.cmake | 72 + app/src/main/jni/badvpn/compile-tun2sock.sh | 112 + .../main/jni/badvpn/dhcpclient/BDHCPClient.c | 340 + .../main/jni/badvpn/dhcpclient/BDHCPClient.h | 87 + .../jni/badvpn/dhcpclient/BDHCPClientCore.c | 860 ++ .../jni/badvpn/dhcpclient/BDHCPClientCore.h | 114 + .../main/jni/badvpn/dhcpclient/CMakeLists.txt | 13 + .../jni/badvpn/dhcpclient/DHCPIpUdpDecoder.c | 137 + .../jni/badvpn/dhcpclient/DHCPIpUdpDecoder.h | 49 + .../jni/badvpn/dhcpclient/DHCPIpUdpEncoder.c | 119 + .../jni/badvpn/dhcpclient/DHCPIpUdpEncoder.h | 49 + .../main/jni/badvpn/dostest/CMakeLists.txt | 10 + .../main/jni/badvpn/dostest/StreamBuffer.c | 147 + .../main/jni/badvpn/dostest/StreamBuffer.h | 70 + .../jni/badvpn/dostest/dostest-attacker.c | 512 + .../main/jni/badvpn/dostest/dostest-server.c | 567 + .../main/jni/badvpn/examples/CMakeLists.txt | 97 + .../jni/badvpn/examples/FastPacketSource.h | 79 + .../jni/badvpn/examples/RandomPacketSink.h | 116 + .../jni/badvpn/examples/TimerPacketSink.h | 97 + .../main/jni/badvpn/examples/arpprobe_test.c | 131 + app/src/main/jni/badvpn/examples/bavl_test.c | 129 + .../jni/badvpn/examples/bencryption_bench.c | 146 + .../jni/badvpn/examples/bprocess_example.c | 140 + .../main/jni/badvpn/examples/brandom2_test.c | 65 + .../main/jni/badvpn/examples/btimer_example.c | 84 + app/src/main/jni/badvpn/examples/cavl_test.c | 286 + .../main/jni/badvpn/examples/cavl_test_tree.h | 23 + .../jni/badvpn/examples/dhcpclient_test.c | 159 + .../jni/badvpn/examples/emscripten_test.c | 71 + .../main/jni/badvpn/examples/fairqueue_test.c | 145 + .../jni/badvpn/examples/fairqueue_test2.c | 93 + .../jni/badvpn/examples/indexedlist_test.c | 95 + .../main/jni/badvpn/examples/ipaddr6_test.c | 169 + .../jni/badvpn/examples/ncd_parser_test.c | 294 + .../jni/badvpn/examples/ncd_tokenizer_test.c | 149 + .../badvpn/examples/ncd_value_parser_test.c | 78 + .../examples/ncdinterfacemonitor_test.c | 150 + .../jni/badvpn/examples/ncdudevmanager_test.c | 161 + .../jni/badvpn/examples/ncdudevmonitor_test.c | 152 + .../main/jni/badvpn/examples/ncdval_test.c | 380 + .../jni/badvpn/examples/ncdvalcons_test.c | 111 + .../jni/badvpn/examples/parse_number_test.c | 130 + .../main/jni/badvpn/examples/predicate_test.c | 116 + app/src/main/jni/badvpn/examples/savl_test.c | 135 + .../main/jni/badvpn/examples/savl_test_tree.h | 9 + .../main/jni/badvpn/examples/stdin_input.c | 138 + .../main/jni/badvpn/examples/substring_test.c | 204 + app/src/main/jni/badvpn/fix_flex.php | 10 + .../main/jni/badvpn/flooder/CMakeLists.txt | 7 + app/src/main/jni/badvpn/flooder/flooder.c | 671 + app/src/main/jni/badvpn/flooder/flooder.h | 37 + app/src/main/jni/badvpn/flow/BufferWriter.c | 112 + app/src/main/jni/badvpn/flow/BufferWriter.h | 107 + app/src/main/jni/badvpn/flow/CMakeLists.txt | 31 + app/src/main/jni/badvpn/flow/LineBuffer.c | 140 + app/src/main/jni/badvpn/flow/LineBuffer.h | 54 + app/src/main/jni/badvpn/flow/PacketBuffer.c | 131 + app/src/main/jni/badvpn/flow/PacketBuffer.h | 77 + app/src/main/jni/badvpn/flow/PacketCopier.c | 136 + app/src/main/jni/badvpn/flow/PacketCopier.h | 90 + .../jni/badvpn/flow/PacketPassConnector.c | 125 + .../jni/badvpn/flow/PacketPassConnector.h | 102 + .../jni/badvpn/flow/PacketPassFairQueue.c | 405 + .../jni/badvpn/flow/PacketPassFairQueue.h | 204 + .../badvpn/flow/PacketPassFairQueue_tree.h | 7 + .../jni/badvpn/flow/PacketPassFifoQueue.c | 241 + .../jni/badvpn/flow/PacketPassFifoQueue.h | 76 + .../jni/badvpn/flow/PacketPassInterface.c | 68 + .../jni/badvpn/flow/PacketPassInterface.h | 236 + .../main/jni/badvpn/flow/PacketPassNotifier.c | 103 + .../main/jni/badvpn/flow/PacketPassNotifier.h | 99 + .../jni/badvpn/flow/PacketPassPriorityQueue.c | 283 + .../jni/badvpn/flow/PacketPassPriorityQueue.h | 192 + .../flow/PacketPassPriorityQueue_tree.h | 7 + .../main/jni/badvpn/flow/PacketProtoDecoder.c | 182 + .../main/jni/badvpn/flow/PacketProtoDecoder.h | 96 + .../main/jni/badvpn/flow/PacketProtoEncoder.c | 101 + .../main/jni/badvpn/flow/PacketProtoEncoder.h | 80 + .../main/jni/badvpn/flow/PacketProtoFlow.c | 82 + .../main/jni/badvpn/flow/PacketProtoFlow.h | 83 + .../main/jni/badvpn/flow/PacketRecvBlocker.c | 99 + .../main/jni/badvpn/flow/PacketRecvBlocker.h | 90 + .../jni/badvpn/flow/PacketRecvConnector.c | 123 + .../jni/badvpn/flow/PacketRecvConnector.h | 102 + .../jni/badvpn/flow/PacketRecvInterface.c | 56 + .../jni/badvpn/flow/PacketRecvInterface.h | 170 + app/src/main/jni/badvpn/flow/PacketRouter.c | 129 + app/src/main/jni/badvpn/flow/PacketRouter.h | 126 + .../main/jni/badvpn/flow/PacketStreamSender.c | 111 + .../main/jni/badvpn/flow/PacketStreamSender.h | 83 + app/src/main/jni/badvpn/flow/RouteBuffer.c | 256 + app/src/main/jni/badvpn/flow/RouteBuffer.h | 139 + .../main/jni/badvpn/flow/SinglePacketBuffer.c | 87 + .../main/jni/badvpn/flow/SinglePacketBuffer.h | 75 + .../main/jni/badvpn/flow/SinglePacketSender.c | 72 + .../main/jni/badvpn/flow/SinglePacketSender.h | 82 + .../jni/badvpn/flow/SingleStreamReceiver.c | 82 + .../jni/badvpn/flow/SingleStreamReceiver.h | 53 + .../main/jni/badvpn/flow/SingleStreamSender.c | 82 + .../main/jni/badvpn/flow/SingleStreamSender.h | 53 + .../main/jni/badvpn/flow/StreamPacketSender.c | 90 + .../main/jni/badvpn/flow/StreamPacketSender.h | 77 + .../jni/badvpn/flow/StreamPassConnector.c | 120 + .../jni/badvpn/flow/StreamPassConnector.h | 98 + .../jni/badvpn/flow/StreamPassInterface.c | 56 + .../jni/badvpn/flow/StreamPassInterface.h | 165 + .../jni/badvpn/flow/StreamRecvConnector.c | 120 + .../jni/badvpn/flow/StreamRecvConnector.h | 98 + .../jni/badvpn/flow/StreamRecvInterface.c | 56 + .../jni/badvpn/flow/StreamRecvInterface.h | 165 + .../main/jni/badvpn/flowextra/CMakeLists.txt | 5 + .../main/jni/badvpn/flowextra/KeepaliveIO.c | 112 + .../main/jni/badvpn/flowextra/KeepaliveIO.h | 88 + .../flowextra/PacketPassInactivityMonitor.c | 131 + .../flowextra/PacketPassInactivityMonitor.h | 124 + app/src/main/jni/badvpn/generate_files | 51 + .../badvpn/generated/NCDConfigParser_parse.c | 1890 +++ .../badvpn/generated/NCDConfigParser_parse.h | 22 + .../generated/NCDConfigParser_parse.out | 950 ++ .../badvpn/generated/NCDConfigParser_parse.y | 718 + .../jni/badvpn/generated/NCDValParser_parse.c | 1119 ++ .../jni/badvpn/generated/NCDValParser_parse.h | 7 + .../badvpn/generated/NCDValParser_parse.out | 217 + .../jni/badvpn/generated/NCDValParser_parse.y | 202 + .../jni/badvpn/generated/bison_BPredicate.c | 2163 +++ .../jni/badvpn/generated/bison_BPredicate.h | 114 + .../badvpn/generated/blog_channel_BArpProbe.h | 4 + .../generated/blog_channel_BConnection.h | 4 + .../generated/blog_channel_BDHCPClient.h | 4 + .../generated/blog_channel_BDHCPClientCore.h | 4 + .../badvpn/generated/blog_channel_BDatagram.h | 4 + .../generated/blog_channel_BEncryption.h | 4 + .../generated/blog_channel_BInputProcess.h | 4 + .../generated/blog_channel_BLockReactor.h | 4 + .../badvpn/generated/blog_channel_BNetwork.h | 4 + .../generated/blog_channel_BPredicate.h | 4 + .../badvpn/generated/blog_channel_BProcess.h | 4 + .../badvpn/generated/blog_channel_BReactor.h | 4 + .../generated/blog_channel_BSSLConnection.h | 4 + .../badvpn/generated/blog_channel_BSignal.h | 4 + .../generated/blog_channel_BSocksClient.h | 4 + .../jni/badvpn/generated/blog_channel_BTap.h | 4 + .../generated/blog_channel_BThreadSignal.h | 4 + .../generated/blog_channel_BThreadWork.h | 4 + .../jni/badvpn/generated/blog_channel_BTime.h | 4 + .../generated/blog_channel_BUnixSignal.h | 4 + .../badvpn/generated/blog_channel_DPReceive.h | 4 + .../badvpn/generated/blog_channel_DPRelay.h | 4 + .../badvpn/generated/blog_channel_DataProto.h | 4 + .../generated/blog_channel_DatagramPeerIO.h | 4 + .../blog_channel_FragmentProtoAssembler.h | 4 + .../generated/blog_channel_FrameDecider.h | 4 + .../generated/blog_channel_LineBuffer.h | 4 + .../badvpn/generated/blog_channel_Listener.h | 4 + .../generated/blog_channel_NCDBuildProgram.h | 4 + .../generated/blog_channel_NCDConfigParser.h | 4 + .../blog_channel_NCDConfigTokenizer.h | 4 + .../generated/blog_channel_NCDIfConfig.h | 4 + .../blog_channel_NCDInterfaceMonitor.h | 4 + .../generated/blog_channel_NCDModuleIndex.h | 4 + .../generated/blog_channel_NCDModuleProcess.h | 4 + .../generated/blog_channel_NCDPlaceholderDb.h | 4 + .../generated/blog_channel_NCDRequest.h | 4 + .../generated/blog_channel_NCDRequestClient.h | 4 + .../generated/blog_channel_NCDRfkillMonitor.h | 4 + .../generated/blog_channel_NCDUdevCache.h | 4 + .../generated/blog_channel_NCDUdevManager.h | 4 + .../generated/blog_channel_NCDUdevMonitor.h | 4 + .../blog_channel_NCDUdevMonitorParser.h | 4 + .../badvpn/generated/blog_channel_NCDVal.h | 4 + .../generated/blog_channel_NCDValGenerator.h | 4 + .../generated/blog_channel_NCDValParser.h | 4 + .../generated/blog_channel_PRStreamSink.h | 4 + .../generated/blog_channel_PRStreamSource.h | 4 + .../blog_channel_PacketProtoDecoder.h | 4 + .../generated/blog_channel_PasswordListener.h | 4 + .../badvpn/generated/blog_channel_PeerChat.h | 4 + .../generated/blog_channel_SPProtoDecoder.h | 4 + .../generated/blog_channel_ServerConnection.h | 4 + .../generated/blog_channel_SocksUdpGwClient.h | 4 + .../generated/blog_channel_StreamPeerIO.h | 4 + .../generated/blog_channel_UdpGwClient.h | 4 + .../jni/badvpn/generated/blog_channel_addr.h | 4 + .../badvpn/generated/blog_channel_client.h | 4 + .../generated/blog_channel_dostest_attacker.h | 4 + .../generated/blog_channel_dostest_server.h | 4 + .../badvpn/generated/blog_channel_flooder.h | 4 + .../jni/badvpn/generated/blog_channel_lwip.h | 4 + .../jni/badvpn/generated/blog_channel_ncd.h | 4 + .../badvpn/generated/blog_channel_ncd_alias.h | 4 + .../generated/blog_channel_ncd_arithmetic.h | 4 + .../generated/blog_channel_ncd_assert.h | 4 + .../generated/blog_channel_ncd_backtrack.h | 4 + .../generated/blog_channel_ncd_blocker.h | 4 + .../generated/blog_channel_ncd_buffer.h | 4 + .../badvpn/generated/blog_channel_ncd_call2.h | 4 + .../generated/blog_channel_ncd_choose.h | 4 + .../generated/blog_channel_ncd_concat.h | 4 + .../generated/blog_channel_ncd_daemon.h | 4 + .../generated/blog_channel_ncd_depend.h | 4 + .../generated/blog_channel_ncd_depend_scope.h | 4 + .../blog_channel_ncd_dynamic_depend.h | 4 + .../badvpn/generated/blog_channel_ncd_exit.h | 4 + .../generated/blog_channel_ncd_explode.h | 4 + .../badvpn/generated/blog_channel_ncd_file.h | 4 + .../generated/blog_channel_ncd_file_open.h | 4 + .../generated/blog_channel_ncd_foreach.h | 4 + .../generated/blog_channel_ncd_from_string.h | 4 + .../generated/blog_channel_ncd_getargs.h | 4 + .../generated/blog_channel_ncd_getenv.h | 4 + .../badvpn/generated/blog_channel_ncd_if.h | 4 + .../generated/blog_channel_ncd_imperative.h | 4 + .../generated/blog_channel_ncd_implode.h | 4 + .../badvpn/generated/blog_channel_ncd_index.h | 4 + .../badvpn/generated/blog_channel_ncd_list.h | 4 + .../generated/blog_channel_ncd_load_module.h | 4 + .../badvpn/generated/blog_channel_ncd_log.h | 4 + .../generated/blog_channel_ncd_log_msg.h | 4 + .../generated/blog_channel_ncd_logical.h | 4 + .../generated/blog_channel_ncd_multidepend.h | 4 + .../blog_channel_ncd_net_backend_badvpn.h | 4 + .../blog_channel_ncd_net_backend_rfkill.h | 4 + .../blog_channel_ncd_net_backend_waitdevice.h | 4 + .../blog_channel_ncd_net_backend_waitlink.h | 4 + ...g_channel_ncd_net_backend_wpa_supplicant.h | 4 + .../generated/blog_channel_ncd_net_dns.h | 4 + .../generated/blog_channel_ncd_net_iptables.h | 4 + .../blog_channel_ncd_net_ipv4_addr.h | 4 + ...log_channel_ncd_net_ipv4_addr_in_network.h | 4 + .../blog_channel_ncd_net_ipv4_arp_probe.h | 4 + .../blog_channel_ncd_net_ipv4_dhcp.h | 4 + .../blog_channel_ncd_net_ipv4_route.h | 4 + .../blog_channel_ncd_net_ipv6_addr.h | 4 + ...log_channel_ncd_net_ipv6_addr_in_network.h | 4 + .../blog_channel_ncd_net_ipv6_route.h | 4 + ...g_channel_ncd_net_ipv6_wait_dynamic_addr.h | 4 + .../generated/blog_channel_ncd_net_up.h | 4 + .../blog_channel_ncd_net_watch_interfaces.h | 4 + .../generated/blog_channel_ncd_netmask.h | 4 + .../generated/blog_channel_ncd_ondemand.h | 4 + .../badvpn/generated/blog_channel_ncd_parse.h | 4 + .../badvpn/generated/blog_channel_ncd_print.h | 4 + .../blog_channel_ncd_process_manager.h | 4 + .../generated/blog_channel_ncd_reboot.h | 4 + .../badvpn/generated/blog_channel_ncd_ref.h | 4 + .../generated/blog_channel_ncd_regex_match.h | 4 + .../generated/blog_channel_ncd_request.h | 4 + .../badvpn/generated/blog_channel_ncd_run.h | 4 + .../generated/blog_channel_ncd_runonce.h | 4 + .../badvpn/generated/blog_channel_ncd_sleep.h | 4 + .../generated/blog_channel_ncd_socket.h | 4 + .../badvpn/generated/blog_channel_ncd_spawn.h | 4 + .../generated/blog_channel_ncd_strcmp.h | 4 + .../generated/blog_channel_ncd_substr.h | 4 + .../generated/blog_channel_ncd_sys_evdev.h | 4 + .../blog_channel_ncd_sys_request_client.h | 4 + .../blog_channel_ncd_sys_request_server.h | 4 + .../blog_channel_ncd_sys_start_process.h | 4 + .../blog_channel_ncd_sys_watch_directory.h | 4 + .../blog_channel_ncd_sys_watch_input.h | 4 + .../blog_channel_ncd_sys_watch_usb.h | 4 + .../badvpn/generated/blog_channel_ncd_timer.h | 4 + .../generated/blog_channel_ncd_to_string.h | 4 + .../badvpn/generated/blog_channel_ncd_try.h | 4 + .../badvpn/generated/blog_channel_ncd_value.h | 4 + .../generated/blog_channel_ncd_valuemetic.h | 4 + .../badvpn/generated/blog_channel_ncd_var.h | 4 + .../badvpn/generated/blog_channel_nsskey.h | 4 + .../badvpn/generated/blog_channel_server.h | 4 + .../badvpn/generated/blog_channel_tun2socks.h | 4 + .../jni/badvpn/generated/blog_channel_udpgw.h | 4 + .../badvpn/generated/blog_channels_defines.h | 146 + .../jni/badvpn/generated/blog_channels_list.h | 145 + .../main/jni/badvpn/generated/bproto_addr.h | 675 + .../jni/badvpn/generated/bproto_bproto_test.h | 1029 ++ .../jni/badvpn/generated/bproto_msgproto.h | 2122 +++ .../jni/badvpn/generated/flex_BPredicate.c | 2143 +++ .../jni/badvpn/generated/flex_BPredicate.h | 350 + app/src/main/jni/badvpn/lemon/lemon.c | 4889 +++++++ app/src/main/jni/badvpn/lemon/lempar.c | 842 ++ app/src/main/jni/badvpn/lime/HOWTO | 70 + .../jni/badvpn/lime/flex_token_stream.php | 34 + app/src/main/jni/badvpn/lime/lemon.c | 4588 +++++++ app/src/main/jni/badvpn/lime/lime.bootstrap | 31 + app/src/main/jni/badvpn/lime/lime.php | 910 ++ .../main/jni/badvpn/lime/lime_scan_tokens.l | 121 + app/src/main/jni/badvpn/lime/metagrammar | 58 + app/src/main/jni/badvpn/lime/parse_engine.php | 252 + app/src/main/jni/badvpn/lime/set.so.php | 29 + app/src/main/jni/badvpn/lwip/CHANGELOG | 3396 +++++ app/src/main/jni/badvpn/lwip/CMakeLists.txt | 26 + app/src/main/jni/badvpn/lwip/COPYING | 33 + app/src/main/jni/badvpn/lwip/FILES | 4 + app/src/main/jni/badvpn/lwip/README | 89 + app/src/main/jni/badvpn/lwip/UPGRADING | 144 + app/src/main/jni/badvpn/lwip/custom/arch/cc.h | 96 + .../main/jni/badvpn/lwip/custom/arch/perf.h | 36 + .../main/jni/badvpn/lwip/custom/lwipopts.h | 70 + app/src/main/jni/badvpn/lwip/custom/sys.c | 37 + app/src/main/jni/badvpn/lwip/doc/FILES | 6 + app/src/main/jni/badvpn/lwip/doc/contrib.txt | 63 + app/src/main/jni/badvpn/lwip/doc/rawapi.txt | 511 + app/src/main/jni/badvpn/lwip/doc/savannah.txt | 135 + .../main/jni/badvpn/lwip/doc/snmp_agent.txt | 181 + app/src/main/jni/badvpn/lwip/doc/sys_arch.txt | 267 + .../main/jni/badvpn/lwip/lwip-base-version | 1 + app/src/main/jni/badvpn/lwip/src/FILES | 13 + .../main/jni/badvpn/lwip/src/api/api_lib.c | 788 ++ .../main/jni/badvpn/lwip/src/api/api_msg.c | 1610 +++ app/src/main/jni/badvpn/lwip/src/api/err.c | 75 + app/src/main/jni/badvpn/lwip/src/api/netbuf.c | 245 + app/src/main/jni/badvpn/lwip/src/api/netdb.c | 353 + .../main/jni/badvpn/lwip/src/api/netifapi.c | 160 + .../main/jni/badvpn/lwip/src/api/sockets.c | 2555 ++++ app/src/main/jni/badvpn/lwip/src/api/tcpip.c | 492 + app/src/main/jni/badvpn/lwip/src/core/def.c | 108 + app/src/main/jni/badvpn/lwip/src/core/dhcp.c | 1771 +++ app/src/main/jni/badvpn/lwip/src/core/dns.c | 988 ++ .../jni/badvpn/lwip/src/core/inet_chksum.c | 545 + app/src/main/jni/badvpn/lwip/src/core/init.c | 345 + .../jni/badvpn/lwip/src/core/ipv4/autoip.c | 528 + .../main/jni/badvpn/lwip/src/core/ipv4/icmp.c | 338 + .../main/jni/badvpn/lwip/src/core/ipv4/igmp.c | 805 ++ .../main/jni/badvpn/lwip/src/core/ipv4/ip4.c | 924 ++ .../jni/badvpn/lwip/src/core/ipv4/ip4_addr.c | 312 + .../jni/badvpn/lwip/src/core/ipv4/ip_frag.c | 863 ++ .../main/jni/badvpn/lwip/src/core/ipv6/README | 1 + .../jni/badvpn/lwip/src/core/ipv6/dhcp6.c | 50 + .../jni/badvpn/lwip/src/core/ipv6/ethip6.c | 193 + .../jni/badvpn/lwip/src/core/ipv6/icmp6.c | 337 + .../jni/badvpn/lwip/src/core/ipv6/inet6.c | 51 + .../main/jni/badvpn/lwip/src/core/ipv6/ip6.c | 1034 ++ .../jni/badvpn/lwip/src/core/ipv6/ip6_addr.c | 251 + .../jni/badvpn/lwip/src/core/ipv6/ip6_frag.c | 697 + .../main/jni/badvpn/lwip/src/core/ipv6/mld6.c | 580 + .../main/jni/badvpn/lwip/src/core/ipv6/nd6.c | 1787 +++ app/src/main/jni/badvpn/lwip/src/core/mem.c | 659 + app/src/main/jni/badvpn/lwip/src/core/memp.c | 485 + app/src/main/jni/badvpn/lwip/src/core/netif.c | 912 ++ app/src/main/jni/badvpn/lwip/src/core/pbuf.c | 1179 ++ app/src/main/jni/badvpn/lwip/src/core/raw.c | 422 + .../jni/badvpn/lwip/src/core/snmp/asn1_dec.c | 657 + .../jni/badvpn/lwip/src/core/snmp/asn1_enc.c | 611 + .../main/jni/badvpn/lwip/src/core/snmp/mib2.c | 4146 ++++++ .../badvpn/lwip/src/core/snmp/mib_structs.c | 1174 ++ .../jni/badvpn/lwip/src/core/snmp/msg_in.c | 1453 ++ .../jni/badvpn/lwip/src/core/snmp/msg_out.c | 678 + app/src/main/jni/badvpn/lwip/src/core/stats.c | 181 + app/src/main/jni/badvpn/lwip/src/core/sys.c | 68 + app/src/main/jni/badvpn/lwip/src/core/tcp.c | 1852 +++ .../main/jni/badvpn/lwip/src/core/tcp_in.c | 1666 +++ .../main/jni/badvpn/lwip/src/core/tcp_out.c | 1499 ++ .../main/jni/badvpn/lwip/src/core/timers.c | 546 + app/src/main/jni/badvpn/lwip/src/core/udp.c | 1151 ++ .../lwip/src/include/ipv4/lwip/autoip.h | 118 + .../badvpn/lwip/src/include/ipv4/lwip/icmp.h | 125 + .../badvpn/lwip/src/include/ipv4/lwip/igmp.h | 106 + .../badvpn/lwip/src/include/ipv4/lwip/inet.h | 107 + .../badvpn/lwip/src/include/ipv4/lwip/ip4.h | 146 + .../lwip/src/include/ipv4/lwip/ip4_addr.h | 244 + .../lwip/src/include/ipv4/lwip/ip_frag.h | 91 + .../badvpn/lwip/src/include/ipv6/lwip/dhcp6.h | 58 + .../lwip/src/include/ipv6/lwip/ethip6.h | 68 + .../badvpn/lwip/src/include/ipv6/lwip/icmp6.h | 152 + .../badvpn/lwip/src/include/ipv6/lwip/inet6.h | 92 + .../badvpn/lwip/src/include/ipv6/lwip/ip6.h | 197 + .../lwip/src/include/ipv6/lwip/ip6_addr.h | 286 + .../lwip/src/include/ipv6/lwip/ip6_frag.h | 102 + .../badvpn/lwip/src/include/ipv6/lwip/mld6.h | 118 + .../badvpn/lwip/src/include/ipv6/lwip/nd6.h | 369 + .../jni/badvpn/lwip/src/include/lwip/api.h | 338 + .../badvpn/lwip/src/include/lwip/api_msg.h | 177 + .../jni/badvpn/lwip/src/include/lwip/arch.h | 217 + .../jni/badvpn/lwip/src/include/lwip/debug.h | 99 + .../jni/badvpn/lwip/src/include/lwip/def.h | 123 + .../jni/badvpn/lwip/src/include/lwip/dhcp.h | 242 + .../jni/badvpn/lwip/src/include/lwip/dns.h | 124 + .../jni/badvpn/lwip/src/include/lwip/err.h | 85 + .../lwip/src/include/lwip/inet_chksum.h | 112 + .../jni/badvpn/lwip/src/include/lwip/init.h | 72 + .../jni/badvpn/lwip/src/include/lwip/ip.h | 254 + .../badvpn/lwip/src/include/lwip/ip_addr.h | 130 + .../jni/badvpn/lwip/src/include/lwip/mem.h | 123 + .../jni/badvpn/lwip/src/include/lwip/memp.h | 116 + .../badvpn/lwip/src/include/lwip/memp_std.h | 135 + .../jni/badvpn/lwip/src/include/lwip/netbuf.h | 112 + .../jni/badvpn/lwip/src/include/lwip/netdb.h | 124 + .../jni/badvpn/lwip/src/include/lwip/netif.h | 393 + .../badvpn/lwip/src/include/lwip/netifapi.h | 108 + .../jni/badvpn/lwip/src/include/lwip/opt.h | 2417 ++++ .../jni/badvpn/lwip/src/include/lwip/pbuf.h | 185 + .../jni/badvpn/lwip/src/include/lwip/raw.h | 131 + .../jni/badvpn/lwip/src/include/lwip/sio.h | 141 + .../jni/badvpn/lwip/src/include/lwip/snmp.h | 367 + .../badvpn/lwip/src/include/lwip/snmp_asn1.h | 101 + .../badvpn/lwip/src/include/lwip/snmp_msg.h | 315 + .../lwip/src/include/lwip/snmp_structs.h | 268 + .../badvpn/lwip/src/include/lwip/sockets.h | 411 + .../jni/badvpn/lwip/src/include/lwip/stats.h | 347 + .../jni/badvpn/lwip/src/include/lwip/sys.h | 336 + .../jni/badvpn/lwip/src/include/lwip/tcp.h | 400 + .../badvpn/lwip/src/include/lwip/tcp_impl.h | 508 + .../jni/badvpn/lwip/src/include/lwip/tcpip.h | 179 + .../jni/badvpn/lwip/src/include/lwip/timers.h | 100 + .../jni/badvpn/lwip/src/include/lwip/udp.h | 215 + .../badvpn/lwip/src/include/netif/etharp.h | 223 + .../badvpn/lwip/src/include/netif/ppp_oe.h | 190 + .../badvpn/lwip/src/include/netif/slipif.h | 81 + .../jni/badvpn/lwip/src/include/posix/netdb.h | 33 + .../lwip/src/include/posix/sys/socket.h | 33 + app/src/main/jni/badvpn/lwip/src/netif/FILES | 29 + .../main/jni/badvpn/lwip/src/netif/etharp.c | 1413 ++ .../jni/badvpn/lwip/src/netif/ethernetif.c | 322 + .../main/jni/badvpn/lwip/src/netif/ppp/auth.c | 1334 ++ .../main/jni/badvpn/lwip/src/netif/ppp/auth.h | 111 + .../main/jni/badvpn/lwip/src/netif/ppp/chap.c | 908 ++ .../main/jni/badvpn/lwip/src/netif/ppp/chap.h | 150 + .../jni/badvpn/lwip/src/netif/ppp/chpms.c | 396 + .../jni/badvpn/lwip/src/netif/ppp/chpms.h | 64 + .../main/jni/badvpn/lwip/src/netif/ppp/fsm.c | 890 ++ .../main/jni/badvpn/lwip/src/netif/ppp/fsm.h | 157 + .../main/jni/badvpn/lwip/src/netif/ppp/ipcp.c | 1411 ++ .../main/jni/badvpn/lwip/src/netif/ppp/ipcp.h | 106 + .../main/jni/badvpn/lwip/src/netif/ppp/lcp.c | 2066 +++ .../main/jni/badvpn/lwip/src/netif/ppp/lcp.h | 151 + .../jni/badvpn/lwip/src/netif/ppp/magic.c | 80 + .../jni/badvpn/lwip/src/netif/ppp/magic.h | 63 + .../main/jni/badvpn/lwip/src/netif/ppp/md5.c | 320 + .../main/jni/badvpn/lwip/src/netif/ppp/md5.h | 55 + .../main/jni/badvpn/lwip/src/netif/ppp/pap.c | 628 + .../main/jni/badvpn/lwip/src/netif/ppp/pap.h | 118 + .../main/jni/badvpn/lwip/src/netif/ppp/ppp.c | 2052 +++ .../main/jni/badvpn/lwip/src/netif/ppp/ppp.h | 201 + .../jni/badvpn/lwip/src/netif/ppp/ppp_impl.h | 363 + .../jni/badvpn/lwip/src/netif/ppp/ppp_oe.c | 1132 ++ .../jni/badvpn/lwip/src/netif/ppp/pppdebug.h | 73 + .../jni/badvpn/lwip/src/netif/ppp/randm.c | 249 + .../jni/badvpn/lwip/src/netif/ppp/randm.h | 81 + .../jni/badvpn/lwip/src/netif/ppp/readme.txt | 21 + .../main/jni/badvpn/lwip/src/netif/ppp/vj.c | 652 + .../main/jni/badvpn/lwip/src/netif/ppp/vj.h | 156 + .../main/jni/badvpn/lwip/src/netif/slipif.c | 546 + .../jni/badvpn/lwip/test/unit/core/test_mem.c | 73 + .../jni/badvpn/lwip/test/unit/core/test_mem.h | 8 + .../badvpn/lwip/test/unit/core/test_pbuf.c | 73 + .../badvpn/lwip/test/unit/core/test_pbuf.h | 8 + .../badvpn/lwip/test/unit/dhcp/test_dhcp.c | 916 ++ .../badvpn/lwip/test/unit/dhcp/test_dhcp.h | 8 + .../lwip/test/unit/etharp/test_etharp.c | 262 + .../lwip/test/unit/etharp/test_etharp.h | 8 + .../jni/badvpn/lwip/test/unit/lwip_check.h | 37 + .../badvpn/lwip/test/unit/lwip_unittests.c | 49 + .../main/jni/badvpn/lwip/test/unit/lwipopts.h | 53 + .../badvpn/lwip/test/unit/tcp/tcp_helper.c | 303 + .../badvpn/lwip/test/unit/tcp/tcp_helper.h | 52 + .../jni/badvpn/lwip/test/unit/tcp/test_tcp.c | 671 + .../jni/badvpn/lwip/test/unit/tcp/test_tcp.h | 8 + .../badvpn/lwip/test/unit/tcp/test_tcp_oos.c | 958 ++ .../badvpn/lwip/test/unit/tcp/test_tcp_oos.h | 8 + .../jni/badvpn/lwip/test/unit/udp/test_udp.c | 68 + .../jni/badvpn/lwip/test/unit/udp/test_udp.h | 8 + app/src/main/jni/badvpn/misc/BRefTarget.h | 114 + app/src/main/jni/badvpn/misc/Utf16Decoder.h | 113 + app/src/main/jni/badvpn/misc/Utf16Encoder.h | 67 + app/src/main/jni/badvpn/misc/Utf8Decoder.h | 143 + app/src/main/jni/badvpn/misc/Utf8Encoder.h | 81 + app/src/main/jni/badvpn/misc/arp_proto.h | 60 + app/src/main/jni/badvpn/misc/array_length.h | 35 + app/src/main/jni/badvpn/misc/balign.h | 76 + app/src/main/jni/badvpn/misc/balloc.h | 248 + app/src/main/jni/badvpn/misc/blimits.h | 60 + app/src/main/jni/badvpn/misc/bsize.h | 117 + app/src/main/jni/badvpn/misc/bsort.h | 69 + app/src/main/jni/badvpn/misc/bstring.h | 140 + app/src/main/jni/badvpn/misc/byteorder.h | 196 + app/src/main/jni/badvpn/misc/cmdline.h | 181 + app/src/main/jni/badvpn/misc/compare.h | 37 + app/src/main/jni/badvpn/misc/concat_strings.h | 85 + app/src/main/jni/badvpn/misc/cstring.h | 347 + app/src/main/jni/badvpn/misc/dead.h | 134 + app/src/main/jni/badvpn/misc/debug.h | 142 + app/src/main/jni/badvpn/misc/debugcounter.h | 118 + app/src/main/jni/badvpn/misc/debugerror.h | 90 + app/src/main/jni/badvpn/misc/dhcp_proto.h | 131 + app/src/main/jni/badvpn/misc/ethernet_proto.h | 52 + app/src/main/jni/badvpn/misc/exparray.h | 101 + app/src/main/jni/badvpn/misc/expstring.h | 161 + app/src/main/jni/badvpn/misc/find_char.h | 58 + app/src/main/jni/badvpn/misc/find_program.h | 69 + app/src/main/jni/badvpn/misc/get_iface_info.h | 110 + app/src/main/jni/badvpn/misc/grow_array.h | 139 + app/src/main/jni/badvpn/misc/hashfun.h | 60 + app/src/main/jni/badvpn/misc/igmp_proto.h | 97 + app/src/main/jni/badvpn/misc/ipaddr.h | 218 + app/src/main/jni/badvpn/misc/ipaddr6.h | 400 + app/src/main/jni/badvpn/misc/ipv4_proto.h | 145 + app/src/main/jni/badvpn/misc/ipv6_proto.h | 86 + app/src/main/jni/badvpn/misc/loggers_string.h | 43 + app/src/main/jni/badvpn/misc/loglevel.h | 80 + app/src/main/jni/badvpn/misc/maxalign.h | 53 + app/src/main/jni/badvpn/misc/merge.h | 36 + app/src/main/jni/badvpn/misc/minmax.h | 56 + app/src/main/jni/badvpn/misc/modadd.h | 59 + app/src/main/jni/badvpn/misc/mswsock.h | 229 + app/src/main/jni/badvpn/misc/nonblocking.h | 51 + app/src/main/jni/badvpn/misc/nsskey.h | 118 + app/src/main/jni/badvpn/misc/offset.h | 51 + .../jni/badvpn/misc/open_standard_streams.h | 54 + app/src/main/jni/badvpn/misc/overflow.h | 66 + app/src/main/jni/badvpn/misc/packed.h | 51 + app/src/main/jni/badvpn/misc/parse_number.h | 304 + app/src/main/jni/badvpn/misc/print_macros.h | 98 + app/src/main/jni/badvpn/misc/read_file.h | 98 + app/src/main/jni/badvpn/misc/read_write_int.h | 181 + app/src/main/jni/badvpn/misc/socks_proto.h | 118 + app/src/main/jni/badvpn/misc/sslsocket.h | 48 + app/src/main/jni/badvpn/misc/stdbuf_cmdline.h | 93 + app/src/main/jni/badvpn/misc/strdup.h | 86 + .../main/jni/badvpn/misc/string_begins_with.h | 96 + app/src/main/jni/badvpn/misc/substring.h | 81 + app/src/main/jni/badvpn/misc/udp_proto.h | 170 + app/src/main/jni/badvpn/misc/unicode_funcs.h | 232 + app/src/main/jni/badvpn/misc/version.h | 41 + app/src/main/jni/badvpn/misc/write_file.h | 104 + .../jni/badvpn/ncd-request/CMakeLists.txt | 9 + .../main/jni/badvpn/ncd-request/ncd-request.c | 224 + app/src/main/jni/badvpn/ncd/CMakeLists.txt | 211 + app/src/main/jni/badvpn/ncd/NCDAst.c | 1022 ++ app/src/main/jni/badvpn/ncd/NCDAst.h | 237 + app/src/main/jni/badvpn/ncd/NCDBuildProgram.c | 316 + app/src/main/jni/badvpn/ncd/NCDBuildProgram.h | 49 + app/src/main/jni/badvpn/ncd/NCDConfigParser.c | 214 + app/src/main/jni/badvpn/ncd/NCDConfigParser.h | 40 + .../jni/badvpn/ncd/NCDConfigParser_parse.y | 718 + .../main/jni/badvpn/ncd/NCDConfigTokenizer.c | 321 + .../main/jni/badvpn/ncd/NCDConfigTokenizer.h | 64 + .../main/jni/badvpn/ncd/NCDInterpProcess.c | 497 + .../main/jni/badvpn/ncd/NCDInterpProcess.h | 100 + app/src/main/jni/badvpn/ncd/NCDInterpProg.c | 140 + app/src/main/jni/badvpn/ncd/NCDInterpProg.h | 63 + .../main/jni/badvpn/ncd/NCDInterpProg_hash.h | 12 + app/src/main/jni/badvpn/ncd/NCDInterpreter.c | 1356 ++ app/src/main/jni/badvpn/ncd/NCDInterpreter.h | 156 + app/src/main/jni/badvpn/ncd/NCDMethodIndex.c | 272 + app/src/main/jni/badvpn/ncd/NCDMethodIndex.h | 135 + .../main/jni/badvpn/ncd/NCDMethodIndex_hash.h | 12 + app/src/main/jni/badvpn/ncd/NCDModule.c | 625 + app/src/main/jni/badvpn/ncd/NCDModule.h | 1011 ++ app/src/main/jni/badvpn/ncd/NCDModuleIndex.c | 372 + app/src/main/jni/badvpn/ncd/NCDModuleIndex.h | 86 + .../jni/badvpn/ncd/NCDModuleIndex_mhash.h | 12 + app/src/main/jni/badvpn/ncd/NCDObject.c | 40 + app/src/main/jni/badvpn/ncd/NCDObject.h | 356 + .../main/jni/badvpn/ncd/NCDPlaceholderDb.c | 127 + .../main/jni/badvpn/ncd/NCDPlaceholderDb.h | 95 + app/src/main/jni/badvpn/ncd/NCDStringIndex.c | 262 + app/src/main/jni/badvpn/ncd/NCDStringIndex.h | 83 + .../main/jni/badvpn/ncd/NCDStringIndex_hash.h | 13 + app/src/main/jni/badvpn/ncd/NCDSugar.c | 253 + app/src/main/jni/badvpn/ncd/NCDSugar.h | 38 + app/src/main/jni/badvpn/ncd/NCDVal.c | 2065 +++ app/src/main/jni/badvpn/ncd/NCDVal.h | 857 ++ app/src/main/jni/badvpn/ncd/NCDValCons.c | 283 + app/src/main/jni/badvpn/ncd/NCDValCons.h | 176 + app/src/main/jni/badvpn/ncd/NCDValGenerator.c | 193 + app/src/main/jni/badvpn/ncd/NCDValGenerator.h | 40 + app/src/main/jni/badvpn/ncd/NCDValParser.c | 225 + app/src/main/jni/badvpn/ncd/NCDValParser.h | 50 + .../main/jni/badvpn/ncd/NCDValParser_parse.y | 202 + app/src/main/jni/badvpn/ncd/NCDVal_maptree.h | 15 + app/src/main/jni/badvpn/ncd/README | 386 + app/src/main/jni/badvpn/ncd/emncd.c | 137 + app/src/main/jni/badvpn/ncd/emncd.html | 320 + .../jni/badvpn/ncd/examples/dbus_start.ncd | 82 + .../badvpn/ncd/examples/dhcpd.conf.template | 11 + .../badvpn/ncd/examples/directory_updater.ncd | 72 + .../main/jni/badvpn/ncd/examples/events.ncd | 101 + .../ncd/examples/igmpproxy.conf.template | 10 + .../badvpn/ncd/examples/make_dhcp_config.ncd | 27 + .../ncd/examples/make_igmpproxy_config.ncd | 53 + .../main/jni/badvpn/ncd/examples/network.ncd | 123 + .../jni/badvpn/ncd/examples/onoff_server.ncdi | 82 + .../badvpn/ncd/examples/onoff_server_test.ncd | 20 + .../badvpn/ncd/examples/tcp_echo_client.ncd | 35 + .../badvpn/ncd/examples/tcp_echo_server.ncd | 40 + .../main/jni/badvpn/ncd/extra/BEventLock.c | 146 + .../main/jni/badvpn/ncd/extra/BEventLock.h | 127 + .../jni/badvpn/ncd/extra/NCDBProcessOpts.c | 154 + .../jni/badvpn/ncd/extra/NCDBProcessOpts.h | 54 + app/src/main/jni/badvpn/ncd/extra/NCDBuf.c | 123 + app/src/main/jni/badvpn/ncd/extra/NCDBuf.h | 61 + .../main/jni/badvpn/ncd/extra/NCDIfConfig.c | 492 + .../main/jni/badvpn/ncd/extra/NCDIfConfig.h | 70 + .../badvpn/ncd/extra/NCDInterfaceMonitor.c | 446 + .../badvpn/ncd/extra/NCDInterfaceMonitor.h | 160 + .../jni/badvpn/ncd/extra/NCDRequestClient.c | 647 + .../jni/badvpn/ncd/extra/NCDRequestClient.h | 111 + .../jni/badvpn/ncd/extra/NCDRfkillMonitor.c | 117 + .../jni/badvpn/ncd/extra/NCDRfkillMonitor.h | 53 + .../main/jni/badvpn/ncd/extra/address_utils.h | 280 + .../main/jni/badvpn/ncd/extra/build_cmdline.c | 111 + .../main/jni/badvpn/ncd/extra/build_cmdline.h | 38 + .../jni/badvpn/ncd/extra/make_fast_names.h | 154 + .../main/jni/badvpn/ncd/extra/value_utils.h | 174 + .../main/jni/badvpn/ncd/include_linux_input.c | 1 + .../main/jni/badvpn/ncd/make_name_indices.h | 104 + app/src/main/jni/badvpn/ncd/modules/alias.c | 148 + .../main/jni/badvpn/ncd/modules/arithmetic.c | 404 + app/src/main/jni/badvpn/ncd/modules/assert.c | 105 + .../main/jni/badvpn/ncd/modules/backtrack.c | 103 + app/src/main/jni/badvpn/ncd/modules/blocker.c | 353 + app/src/main/jni/badvpn/ncd/modules/buffer.c | 619 + .../badvpn/ncd/modules/buffer_chunks_tree.h | 9 + app/src/main/jni/badvpn/ncd/modules/call2.c | 498 + app/src/main/jni/badvpn/ncd/modules/choose.c | 145 + .../jni/badvpn/ncd/modules/command_template.c | 218 + .../jni/badvpn/ncd/modules/command_template.h | 62 + app/src/main/jni/badvpn/ncd/modules/concat.c | 189 + app/src/main/jni/badvpn/ncd/modules/daemon.c | 296 + app/src/main/jni/badvpn/ncd/modules/depend.c | 452 + .../jni/badvpn/ncd/modules/depend_scope.c | 466 + .../jni/badvpn/ncd/modules/dynamic_depend.c | 548 + .../jni/badvpn/ncd/modules/event_template.c | 184 + .../jni/badvpn/ncd/modules/event_template.h | 64 + app/src/main/jni/badvpn/ncd/modules/exit.c | 91 + app/src/main/jni/badvpn/ncd/modules/explode.c | 232 + app/src/main/jni/badvpn/ncd/modules/file.c | 350 + .../main/jni/badvpn/ncd/modules/file_open.c | 585 + app/src/main/jni/badvpn/ncd/modules/foreach.c | 715 + .../main/jni/badvpn/ncd/modules/from_string.c | 125 + app/src/main/jni/badvpn/ncd/modules/getargs.c | 98 + app/src/main/jni/badvpn/ncd/modules/getenv.c | 153 + app/src/main/jni/badvpn/ncd/modules/if.c | 103 + .../main/jni/badvpn/ncd/modules/imperative.c | 324 + app/src/main/jni/badvpn/ncd/modules/implode.c | 155 + app/src/main/jni/badvpn/ncd/modules/index.c | 164 + app/src/main/jni/badvpn/ncd/modules/list.c | 871 ++ .../main/jni/badvpn/ncd/modules/load_module.c | 313 + app/src/main/jni/badvpn/ncd/modules/log.c | 285 + app/src/main/jni/badvpn/ncd/modules/logical.c | 160 + app/src/main/jni/badvpn/ncd/modules/modules.h | 210 + .../main/jni/badvpn/ncd/modules/multidepend.c | 401 + .../badvpn/ncd/modules/net_backend_badvpn.c | 281 + .../badvpn/ncd/modules/net_backend_rfkill.c | 216 + .../ncd/modules/net_backend_waitdevice.c | 187 + .../badvpn/ncd/modules/net_backend_waitlink.c | 155 + .../ncd/modules/net_backend_wpa_supplicant.c | 563 + app/src/main/jni/badvpn/ncd/modules/net_dns.c | 315 + .../jni/badvpn/ncd/modules/net_iptables.c | 749 + .../jni/badvpn/ncd/modules/net_ipv4_addr.c | 148 + .../ncd/modules/net_ipv4_addr_in_network.c | 173 + .../badvpn/ncd/modules/net_ipv4_arp_probe.c | 212 + .../jni/badvpn/ncd/modules/net_ipv4_dhcp.c | 351 + .../jni/badvpn/ncd/modules/net_ipv4_route.c | 211 + .../jni/badvpn/ncd/modules/net_ipv6_addr.c | 148 + .../ncd/modules/net_ipv6_addr_in_network.c | 168 + .../jni/badvpn/ncd/modules/net_ipv6_route.c | 213 + .../ncd/modules/net_ipv6_wait_dynamic_addr.c | 201 + app/src/main/jni/badvpn/ncd/modules/net_up.c | 119 + .../badvpn/ncd/modules/net_watch_interfaces.c | 474 + app/src/main/jni/badvpn/ncd/modules/netmask.c | 263 + .../main/jni/badvpn/ncd/modules/ondemand.c | 372 + app/src/main/jni/badvpn/ncd/modules/parse.c | 392 + app/src/main/jni/badvpn/ncd/modules/print.c | 207 + .../jni/badvpn/ncd/modules/process_manager.c | 538 + app/src/main/jni/badvpn/ncd/modules/reboot.c | 103 + app/src/main/jni/badvpn/ncd/modules/ref.c | 215 + .../main/jni/badvpn/ncd/modules/regex_match.c | 369 + app/src/main/jni/badvpn/ncd/modules/run.c | 187 + app/src/main/jni/badvpn/ncd/modules/runonce.c | 331 + app/src/main/jni/badvpn/ncd/modules/sleep.c | 178 + app/src/main/jni/badvpn/ncd/modules/socket.c | 1057 ++ app/src/main/jni/badvpn/ncd/modules/spawn.c | 410 + app/src/main/jni/badvpn/ncd/modules/strcmp.c | 107 + app/src/main/jni/badvpn/ncd/modules/substr.c | 167 + .../main/jni/badvpn/ncd/modules/sys_evdev.c | 348 + .../badvpn/ncd/modules/sys_request_client.c | 646 + .../badvpn/ncd/modules/sys_request_server.c | 792 ++ .../badvpn/ncd/modules/sys_start_process.c | 1266 ++ .../badvpn/ncd/modules/sys_watch_directory.c | 425 + .../jni/badvpn/ncd/modules/sys_watch_input.c | 455 + .../jni/badvpn/ncd/modules/sys_watch_usb.c | 421 + app/src/main/jni/badvpn/ncd/modules/timer.c | 146 + .../main/jni/badvpn/ncd/modules/to_string.c | 116 + app/src/main/jni/badvpn/ncd/modules/try.c | 302 + app/src/main/jni/badvpn/ncd/modules/value.c | 2102 +++ .../jni/badvpn/ncd/modules/value_maptree.h | 11 + .../main/jni/badvpn/ncd/modules/valuemetic.c | 219 + app/src/main/jni/badvpn/ncd/modules/var.c | 163 + app/src/main/jni/badvpn/ncd/ncd.c | 463 + app/src/main/jni/badvpn/ncd/ncd.h | 37 + .../main/jni/badvpn/ncd/parse_linux_input.sh | 94 + app/src/main/jni/badvpn/ncd/static_strings.h | 70 + .../jni/badvpn/ncd/tests/addr_in_network.ncd | 60 + app/src/main/jni/badvpn/ncd/tests/alias.ncd | 48 + .../main/jni/badvpn/ncd/tests/arithmetic.ncd | 69 + .../jni/badvpn/ncd/tests/backtracking.ncd | 31 + app/src/main/jni/badvpn/ncd/tests/buffer.ncd | 54 + app/src/main/jni/badvpn/ncd/tests/call.ncd | 18 + app/src/main/jni/badvpn/ncd/tests/concat.ncd | 19 + app/src/main/jni/badvpn/ncd/tests/depend.ncd | 64 + .../jni/badvpn/ncd/tests/depend_scope.ncd | 31 + .../jni/badvpn/ncd/tests/escape_and_nulls.ncd | 38 + app/src/main/jni/badvpn/ncd/tests/explode.ncd | 23 + app/src/main/jni/badvpn/ncd/tests/foreach.ncd | 35 + app/src/main/jni/badvpn/ncd/tests/if.ncd | 38 + app/src/main/jni/badvpn/ncd/tests/implode.ncd | 15 + app/src/main/jni/badvpn/ncd/tests/include.ncd | 16 + .../badvpn/ncd/tests/include_included.ncdi | 5 + .../badvpn/ncd/tests/include_included2.ncdi | 5 + app/src/main/jni/badvpn/ncd/tests/logical.ncd | 46 + .../main/jni/badvpn/ncd/tests/multidepend.ncd | 30 + app/src/main/jni/badvpn/ncd/tests/netmask.ncd | 15 + app/src/main/jni/badvpn/ncd/tests/parse.ncd | 85 + .../jni/badvpn/ncd/tests/process_manager.ncd | 112 + app/src/main/jni/badvpn/ncd/tests/regex.ncd | 48 + app/src/main/jni/badvpn/ncd/tests/run_tests | 38 + app/src/main/jni/badvpn/ncd/tests/strings.ncd | 47 + app/src/main/jni/badvpn/ncd/tests/substr.ncd | 37 + app/src/main/jni/badvpn/ncd/tests/turing.ncd | 138 + app/src/main/jni/badvpn/ncd/tests/value.ncd | 258 + .../jni/badvpn/ncd/tests/value_substr.ncd | 25 + .../jni/badvpn/nspr_support/BSSLConnection.c | 1024 ++ .../jni/badvpn/nspr_support/BSSLConnection.h | 116 + .../jni/badvpn/nspr_support/CMakeLists.txt | 5 + .../jni/badvpn/nspr_support/DummyPRFileDesc.c | 176 + .../jni/badvpn/nspr_support/DummyPRFileDesc.h | 61 + .../main/jni/badvpn/predicate/BPredicate.c | 284 + .../main/jni/badvpn/predicate/BPredicate.h | 177 + .../main/jni/badvpn/predicate/BPredicate.l | 83 + .../main/jni/badvpn/predicate/BPredicate.y | 345 + .../badvpn/predicate/BPredicate_internal.h | 154 + .../jni/badvpn/predicate/BPredicate_parser.h | 47 + .../main/jni/badvpn/predicate/CMakeLists.txt | 6 + .../badvpn/predicate/LexMemoryBufferInput.h | 86 + app/src/main/jni/badvpn/protocol/addr.bproto | 11 + app/src/main/jni/badvpn/protocol/addr.h | 207 + app/src/main/jni/badvpn/protocol/dataproto.h | 91 + .../main/jni/badvpn/protocol/fragmentproto.h | 100 + .../main/jni/badvpn/protocol/msgproto.bproto | 43 + app/src/main/jni/badvpn/protocol/msgproto.h | 76 + .../main/jni/badvpn/protocol/packetproto.h | 68 + .../main/jni/badvpn/protocol/requestproto.h | 50 + app/src/main/jni/badvpn/protocol/scproto.h | 266 + app/src/main/jni/badvpn/protocol/spproto.h | 195 + .../main/jni/badvpn/protocol/udpgw_proto.h | 98 + app/src/main/jni/badvpn/random/BRandom2.c | 90 + app/src/main/jni/badvpn/random/BRandom2.h | 50 + app/src/main/jni/badvpn/random/CMakeLists.txt | 4 + app/src/main/jni/badvpn/scripts/cmake | 8 + app/src/main/jni/badvpn/scripts/copy_nss | 23 + .../main/jni/badvpn/scripts/toolchain.cmake | 6 + .../main/jni/badvpn/security/BEncryption.c | 240 + .../main/jni/badvpn/security/BEncryption.h | 175 + app/src/main/jni/badvpn/security/BHash.c | 69 + app/src/main/jni/badvpn/security/BHash.h | 80 + app/src/main/jni/badvpn/security/BRandom.c | 42 + app/src/main/jni/badvpn/security/BRandom.h | 49 + app/src/main/jni/badvpn/security/BSecurity.c | 149 + app/src/main/jni/badvpn/security/BSecurity.h | 60 + .../main/jni/badvpn/security/CMakeLists.txt | 10 + .../main/jni/badvpn/security/OTPCalculator.c | 118 + .../main/jni/badvpn/security/OTPCalculator.h | 96 + app/src/main/jni/badvpn/security/OTPChecker.c | 297 + app/src/main/jni/badvpn/security/OTPChecker.h | 148 + .../main/jni/badvpn/security/OTPGenerator.c | 159 + .../main/jni/badvpn/security/OTPGenerator.h | 134 + app/src/main/jni/badvpn/server/CMakeLists.txt | 12 + .../main/jni/badvpn/server/badvpn-server.8 | 190 + app/src/main/jni/badvpn/server/server.c | 2389 ++++ app/src/main/jni/badvpn/server/server.h | 186 + .../badvpn/server_connection/CMakeLists.txt | 5 + .../server_connection/SCKeepaliveSource.c | 69 + .../server_connection/SCKeepaliveSource.h | 72 + .../server_connection/ServerConnection.c | 669 + .../server_connection/ServerConnection.h | 289 + .../jni/badvpn/socksclient/BSocksClient.c | 608 + .../jni/badvpn/socksclient/BSocksClient.h | 147 + .../jni/badvpn/socksclient/CMakeLists.txt | 4 + .../main/jni/badvpn/stringmap/BStringMap.c | 198 + .../main/jni/badvpn/stringmap/BStringMap.h | 57 + .../main/jni/badvpn/stringmap/CMakeLists.txt | 3 + app/src/main/jni/badvpn/structure/BAVL.h | 797 ++ app/src/main/jni/badvpn/structure/CAvl.h | 36 + app/src/main/jni/badvpn/structure/CAvl_decl.h | 77 + .../main/jni/badvpn/structure/CAvl_footer.h | 113 + .../main/jni/badvpn/structure/CAvl_header.h | 141 + app/src/main/jni/badvpn/structure/CAvl_impl.h | 949 ++ app/src/main/jni/badvpn/structure/CHash.h | 39 + .../main/jni/badvpn/structure/CHash_decl.h | 59 + .../main/jni/badvpn/structure/CHash_footer.h | 74 + .../main/jni/badvpn/structure/CHash_header.h | 78 + .../main/jni/badvpn/structure/CHash_impl.h | 312 + .../main/jni/badvpn/structure/ChunkBuffer2.h | 317 + .../main/jni/badvpn/structure/IndexedList.h | 225 + .../jni/badvpn/structure/IndexedList_tree.h | 15 + .../main/jni/badvpn/structure/LinkedList0.h | 202 + .../main/jni/badvpn/structure/LinkedList1.h | 275 + .../main/jni/badvpn/structure/LinkedList3.h | 362 + app/src/main/jni/badvpn/structure/SAvl.h | 40 + app/src/main/jni/badvpn/structure/SAvl_decl.h | 73 + .../main/jni/badvpn/structure/SAvl_footer.h | 89 + .../main/jni/badvpn/structure/SAvl_header.h | 93 + app/src/main/jni/badvpn/structure/SAvl_impl.h | 164 + app/src/main/jni/badvpn/structure/SAvl_tree.h | 18 + .../main/jni/badvpn/structure/SLinkedList.h | 38 + .../jni/badvpn/structure/SLinkedList_decl.h | 61 + .../jni/badvpn/structure/SLinkedList_footer.h | 53 + .../jni/badvpn/structure/SLinkedList_header.h | 58 + .../jni/badvpn/structure/SLinkedList_impl.h | 151 + app/src/main/jni/badvpn/system/BAddr.h | 808 ++ app/src/main/jni/badvpn/system/BConnection.h | 369 + .../jni/badvpn/system/BConnectionGeneric.h | 144 + .../main/jni/badvpn/system/BConnection_unix.c | 1057 ++ .../main/jni/badvpn/system/BConnection_unix.h | 87 + .../main/jni/badvpn/system/BConnection_win.c | 875 ++ .../main/jni/badvpn/system/BConnection_win.h | 101 + app/src/main/jni/badvpn/system/BDatagram.h | 209 + .../main/jni/badvpn/system/BDatagram_unix.c | 855 ++ .../main/jni/badvpn/system/BDatagram_unix.h | 71 + .../main/jni/badvpn/system/BDatagram_win.c | 755 + .../main/jni/badvpn/system/BDatagram_win.h | 99 + .../main/jni/badvpn/system/BInputProcess.c | 211 + .../main/jni/badvpn/system/BInputProcess.h | 65 + app/src/main/jni/badvpn/system/BLockReactor.c | 131 + app/src/main/jni/badvpn/system/BLockReactor.h | 58 + app/src/main/jni/badvpn/system/BNetwork.c | 99 + app/src/main/jni/badvpn/system/BNetwork.h | 36 + app/src/main/jni/badvpn/system/BProcess.c | 400 + app/src/main/jni/badvpn/system/BProcess.h | 200 + app/src/main/jni/badvpn/system/BReactor.h | 11 + .../main/jni/badvpn/system/BReactor_badvpn.c | 1430 ++ .../main/jni/badvpn/system/BReactor_badvpn.h | 572 + .../system/BReactor_badvpn_timerstree.h | 13 + .../jni/badvpn/system/BReactor_emscripten.c | 176 + .../jni/badvpn/system/BReactor_emscripten.h | 87 + .../main/jni/badvpn/system/BReactor_glib.c | 524 + .../main/jni/badvpn/system/BReactor_glib.h | 148 + app/src/main/jni/badvpn/system/BSignal.c | 188 + app/src/main/jni/badvpn/system/BSignal.h | 64 + .../main/jni/badvpn/system/BThreadSignal.c | 136 + .../main/jni/badvpn/system/BThreadSignal.h | 53 + app/src/main/jni/badvpn/system/BTime.c | 38 + app/src/main/jni/badvpn/system/BTime.h | 163 + app/src/main/jni/badvpn/system/BUnixSignal.c | 406 + app/src/main/jni/badvpn/system/BUnixSignal.h | 132 + app/src/main/jni/badvpn/system/CMakeLists.txt | 44 + app/src/main/jni/badvpn/tests/CMakeLists.txt | 8 + .../main/jni/badvpn/tests/bproto_test.bproto | 9 + app/src/main/jni/badvpn/tests/bproto_test.c | 76 + .../main/jni/badvpn/tests/chunkbuffer2_test.c | 86 + .../main/jni/badvpn/tests/threadwork_test.c | 87 + .../main/jni/badvpn/threadwork/BThreadWork.c | 451 + .../main/jni/badvpn/threadwork/BThreadWork.h | 171 + .../main/jni/badvpn/threadwork/CMakeLists.txt | 7 + .../main/jni/badvpn/tun2socks/CMakeLists.txt | 15 + .../jni/badvpn/tun2socks/SocksUdpGwClient.c | 607 + .../jni/badvpn/tun2socks/SocksUdpGwClient.h | 106 + .../jni/badvpn/tun2socks/badvpn-tun2socks.8 | 126 + app/src/main/jni/badvpn/tun2socks/tun2socks.c | 2379 ++++ app/src/main/jni/badvpn/tun2socks/tun2socks.h | 46 + app/src/main/jni/badvpn/tunctl/CMakeLists.txt | 6 + app/src/main/jni/badvpn/tunctl/tunctl.c | 352 + app/src/main/jni/badvpn/tuntap/BTap.c | 577 + app/src/main/jni/badvpn/tuntap/BTap.h | 196 + app/src/main/jni/badvpn/tuntap/CMakeLists.txt | 10 + .../main/jni/badvpn/tuntap/tapwin32-funcs.c | 227 + .../main/jni/badvpn/tuntap/tapwin32-funcs.h | 42 + .../main/jni/badvpn/tuntap/wintap-common.h | 39 + .../jni/badvpn/udevmonitor/CMakeLists.txt | 7 + .../jni/badvpn/udevmonitor/NCDUdevCache.c | 417 + .../jni/badvpn/udevmonitor/NCDUdevCache.h | 66 + .../jni/badvpn/udevmonitor/NCDUdevManager.c | 547 + .../jni/badvpn/udevmonitor/NCDUdevManager.h | 84 + .../jni/badvpn/udevmonitor/NCDUdevMonitor.c | 250 + .../jni/badvpn/udevmonitor/NCDUdevMonitor.h | 71 + .../badvpn/udevmonitor/NCDUdevMonitorParser.c | 358 + .../badvpn/udevmonitor/NCDUdevMonitorParser.h | 76 + app/src/main/jni/badvpn/udpgw/CMakeLists.txt | 9 + app/src/main/jni/badvpn/udpgw/udpgw.c | 1473 ++ app/src/main/jni/badvpn/udpgw/udpgw.h | 52 + .../jni/badvpn/udpgw_client/CMakeLists.txt | 2 + .../jni/badvpn/udpgw_client/UdpGwClient.c | 597 + .../jni/badvpn/udpgw_client/UdpGwClient.h | 118 + app/src/main/jni/libancillary/API | 139 + app/src/main/jni/libancillary/COPYING | 21 + app/src/main/jni/libancillary/Makefile | 73 + app/src/main/jni/libancillary/ancillary.h | 131 + app/src/main/jni/libancillary/fd_recv.c | 98 + app/src/main/jni/libancillary/fd_send.c | 92 + app/src/main/jni/libancillary/test.c | 112 + app/src/main/jni/pdnsd/AUTHORS | 58 + app/src/main/jni/pdnsd/COPYING | 674 + app/src/main/jni/pdnsd/COPYING.BSD | 26 + app/src/main/jni/pdnsd/ChangeLog | 3304 +++++ app/src/main/jni/pdnsd/ChangeLog.old | 161 + app/src/main/jni/pdnsd/INSTALL | 190 + app/src/main/jni/pdnsd/Makefile.am | 40 + app/src/main/jni/pdnsd/Makefile.in | 734 + app/src/main/jni/pdnsd/NEWS | 324 + app/src/main/jni/pdnsd/PKGBUILD.in | 24 + app/src/main/jni/pdnsd/README | 22 + app/src/main/jni/pdnsd/README.par | 216 + app/src/main/jni/pdnsd/README.par.old | 249 + app/src/main/jni/pdnsd/THANKS | 66 + app/src/main/jni/pdnsd/TODO | 20 + app/src/main/jni/pdnsd/acconfig.h | 187 + app/src/main/jni/pdnsd/aclocal.m4 | 1021 ++ app/src/main/jni/pdnsd/compile | 142 + app/src/main/jni/pdnsd/config.h | 431 + app/src/main/jni/pdnsd/config.h.in | 426 + app/src/main/jni/pdnsd/configure | 11378 ++++++++++++++++ app/src/main/jni/pdnsd/configure.in | 541 + app/src/main/jni/pdnsd/contrib/Makefile.am | 2 + app/src/main/jni/pdnsd/contrib/Makefile.in | 323 + app/src/main/jni/pdnsd/contrib/README | 30 + .../pdnsd/contrib/change_pdnsd_server_ip.pl | 124 + app/src/main/jni/pdnsd/contrib/dhcp2pdnsd | 45 + app/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl | 246 + app/src/main/jni/pdnsd/depcomp | 530 + app/src/main/jni/pdnsd/doc/Makefile.am | 28 + app/src/main/jni/pdnsd/doc/Makefile.in | 483 + app/src/main/jni/pdnsd/doc/doc_makefile | 38 + app/src/main/jni/pdnsd/doc/html/dl.html | 96 + app/src/main/jni/pdnsd/doc/html/dl.html.in | 96 + app/src/main/jni/pdnsd/doc/html/doc.html | 2630 ++++ app/src/main/jni/pdnsd/doc/html/doc_makefile | 23 + app/src/main/jni/pdnsd/doc/html/faq.html | 412 + app/src/main/jni/pdnsd/doc/html/htmlsubst.pl | 36 + app/src/main/jni/pdnsd/doc/html/index.html | 686 + app/src/main/jni/pdnsd/doc/html2confman.pl | 161 + app/src/main/jni/pdnsd/doc/pdnsd-ctl.8 | 198 + app/src/main/jni/pdnsd/doc/pdnsd.8.in | 326 + app/src/main/jni/pdnsd/doc/pdnsd.conf.5.in | 1328 ++ app/src/main/jni/pdnsd/doc/pdnsd.conf.in | 143 + app/src/main/jni/pdnsd/doc/txt/doc_makefile | 21 + app/src/main/jni/pdnsd/doc/txt/faq.txt | 227 + app/src/main/jni/pdnsd/doc/txt/intro.txt | 305 + app/src/main/jni/pdnsd/doc/txt/manual.txt | 2017 +++ app/src/main/jni/pdnsd/file-list.base.in | 5 + app/src/main/jni/pdnsd/install-sh | 323 + app/src/main/jni/pdnsd/missing | 360 + app/src/main/jni/pdnsd/pdnsd.spec.in | 244 + app/src/main/jni/pdnsd/src/Makefile.am | 24 + app/src/main/jni/pdnsd/src/Makefile.in | 921 ++ app/src/main/jni/pdnsd/src/cache.c | 2723 ++++ app/src/main/jni/pdnsd/src/cache.h | 306 + app/src/main/jni/pdnsd/src/conf-keywords.h | 238 + app/src/main/jni/pdnsd/src/conf-parser.c | 2118 +++ app/src/main/jni/pdnsd/src/conf-parser.h | 29 + app/src/main/jni/pdnsd/src/conff.c | 544 + app/src/main/jni/pdnsd/src/conff.h | 190 + app/src/main/jni/pdnsd/src/consts.c | 133 + app/src/main/jni/pdnsd/src/consts.h | 69 + app/src/main/jni/pdnsd/src/debug.c | 64 + app/src/main/jni/pdnsd/src/debug.h | 52 + app/src/main/jni/pdnsd/src/dns.c | 617 + app/src/main/jni/pdnsd/src/dns.h | 309 + app/src/main/jni/pdnsd/src/dns_answer.c | 2170 +++ app/src/main/jni/pdnsd/src/dns_answer.h | 40 + app/src/main/jni/pdnsd/src/dns_query.c | 3798 ++++++ app/src/main/jni/pdnsd/src/dns_query.h | 51 + app/src/main/jni/pdnsd/src/error.c | 142 + app/src/main/jni/pdnsd/src/error.h | 115 + .../jni/pdnsd/src/freebsd_netinet_ip_icmp.h | 187 + app/src/main/jni/pdnsd/src/hash.c | 322 + app/src/main/jni/pdnsd/src/hash.h | 83 + app/src/main/jni/pdnsd/src/helpers.c | 795 ++ app/src/main/jni/pdnsd/src/helpers.h | 319 + app/src/main/jni/pdnsd/src/icmp.c | 544 + app/src/main/jni/pdnsd/src/icmp.h | 43 + app/src/main/jni/pdnsd/src/ipvers.h | 297 + app/src/main/jni/pdnsd/src/list.c | 171 + app/src/main/jni/pdnsd/src/list.h | 170 + app/src/main/jni/pdnsd/src/main.c | 717 + app/src/main/jni/pdnsd/src/make_rr_types_h.pl | 309 + app/src/main/jni/pdnsd/src/netdev.c | 363 + app/src/main/jni/pdnsd/src/netdev.h | 32 + .../main/jni/pdnsd/src/pdnsd-ctl/Makefile.am | 18 + .../main/jni/pdnsd/src/pdnsd-ctl/Makefile.in | 470 + .../main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c | 802 ++ app/src/main/jni/pdnsd/src/pdnsd_assert.h | 51 + .../jni/pdnsd/src/rc/ArchLinux/Makefile.am | 7 + .../jni/pdnsd/src/rc/ArchLinux/Makefile.in | 332 + .../main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in | 45 + .../main/jni/pdnsd/src/rc/Debian/Makefile.am | 8 + .../main/jni/pdnsd/src/rc/Debian/Makefile.in | 334 + app/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in | 52 + app/src/main/jni/pdnsd/src/rc/Makefile.am | 5 + app/src/main/jni/pdnsd/src/rc/Makefile.in | 526 + app/src/main/jni/pdnsd/src/rc/README | 104 + .../main/jni/pdnsd/src/rc/RedHat/Makefile.am | 12 + .../main/jni/pdnsd/src/rc/RedHat/Makefile.in | 337 + app/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in | 88 + .../jni/pdnsd/src/rc/Slackware/Makefile.am | 3 + .../jni/pdnsd/src/rc/Slackware/Makefile.in | 330 + .../jni/pdnsd/src/rc/Slackware/rc.pdnsd.in | 74 + .../main/jni/pdnsd/src/rc/SuSE/Makefile.am | 22 + .../main/jni/pdnsd/src/rc/SuSE/Makefile.in | 345 + app/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in | 68 + app/src/main/jni/pdnsd/src/rr_types.c | 172 + app/src/main/jni/pdnsd/src/rr_types.h | 536 + app/src/main/jni/pdnsd/src/rr_types.in | 99 + app/src/main/jni/pdnsd/src/servers.c | 856 ++ app/src/main/jni/pdnsd/src/servers.h | 68 + app/src/main/jni/pdnsd/src/sort_namevalues.pl | 25 + app/src/main/jni/pdnsd/src/status.c | 824 ++ app/src/main/jni/pdnsd/src/status.h | 59 + app/src/main/jni/pdnsd/src/test/Makefile.am | 35 + app/src/main/jni/pdnsd/src/test/Makefile.in | 464 + app/src/main/jni/pdnsd/src/test/if_up.c | 36 + .../main/jni/pdnsd/src/test/is_local_addr.c | 57 + app/src/main/jni/pdnsd/src/test/random.c | 33 + app/src/main/jni/pdnsd/src/test/tping.c | 59 + app/src/main/jni/pdnsd/src/thread.c | 85 + app/src/main/jni/pdnsd/src/thread.h | 143 + app/src/main/jni/pdnsd/version | 1 + app/src/main/jni/system.cpp | 133 + app/src/main/jniLibs/arm64-v8a/libpdnsd.so | Bin 0 -> 215632 bytes app/src/main/jniLibs/arm64-v8a/libsystem.so | Bin 0 -> 10128 bytes .../main/jniLibs/arm64-v8a/libtun2socks.so | Bin 0 -> 160232 bytes app/src/main/jniLibs/armeabi-v7a/libpdnsd.so | Bin 0 -> 153640 bytes app/src/main/jniLibs/armeabi-v7a/libsystem.so | Bin 0 -> 14020 bytes .../main/jniLibs/armeabi-v7a/libtun2socks.so | Bin 0 -> 101276 bytes app/src/main/jniLibs/x86/libpdnsd.so | Bin 0 -> 198640 bytes app/src/main/jniLibs/x86/libsystem.so | Bin 0 -> 9764 bytes app/src/main/jniLibs/x86/libtun2socks.so | Bin 0 -> 154468 bytes app/src/main/jniLibs/x86_64/libpdnsd.so | Bin 0 -> 219920 bytes app/src/main/jniLibs/x86_64/libsystem.so | Bin 0 -> 10408 bytes app/src/main/jniLibs/x86_64/libtun2socks.so | Bin 0 -> 156328 bytes .../main/res/drawable-anydpi-v24/ic_vpn.xml | 13 + app/src/main/res/drawable-hdpi/ic_vpn.png | Bin 0 -> 662 bytes app/src/main/res/drawable-mdpi/ic_vpn.png | Bin 0 -> 452 bytes app/src/main/res/drawable-xhdpi/ic_vpn.png | Bin 0 -> 861 bytes app/src/main/res/drawable-xxhdpi/ic_vpn.png | Bin 0 -> 1245 bytes .../res/drawable/ic_launcher_foreground.xml | 13 + app/src/main/res/layout/action_switch.xml | 13 + app/src/main/res/menu/main.xml | 9 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1085 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 2900 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 796 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 1807 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 1455 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4043 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 2207 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 6372 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 3188 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 9378 bytes app/src/main/res/values-vi/strings.xml | 43 + app/src/main/res/values/arrays.xml | 12 + .../res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/pdnsd.xml | 32 + app/src/main/res/values/simpleroute.xml | 1154 ++ app/src/main/res/values/strings.xml | 49 + app/src/main/res/values/styles.xml | 8 + app/src/main/res/xml/backup_descriptor.xml | 3 + .../main/res/xml/data_extraction_rules.xml | 6 + app/src/main/res/xml/settings.xml | 117 + build.gradle | 21 + gradle.properties | 3 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 + gradlew.bat | 84 + settings.gradle | 1 + 1174 files changed, 289356 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/.gitignore create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/aidl/net/typeblog/socks/IVpnService.aidl create mode 100755 app/src/main/build-jni.sh create mode 100644 app/src/main/ic_launcher-web.png create mode 100644 app/src/main/java/net/typeblog/socks/BootReceiver.java create mode 100644 app/src/main/java/net/typeblog/socks/MainActivity.java create mode 100644 app/src/main/java/net/typeblog/socks/ProfileFragment.java create mode 100644 app/src/main/java/net/typeblog/socks/SocksVpnService.java create mode 100644 app/src/main/java/net/typeblog/socks/System.java create mode 100644 app/src/main/java/net/typeblog/socks/util/Constants.java create mode 100644 app/src/main/java/net/typeblog/socks/util/Profile.java create mode 100644 app/src/main/java/net/typeblog/socks/util/ProfileManager.java create mode 100644 app/src/main/java/net/typeblog/socks/util/Routes.java create mode 100644 app/src/main/java/net/typeblog/socks/util/Utility.java create mode 100644 app/src/main/jni/Android.mk create mode 100644 app/src/main/jni/Application.mk create mode 100644 app/src/main/jni/badvpn/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/COPYING create mode 100644 app/src/main/jni/badvpn/ChangeLog create mode 100644 app/src/main/jni/badvpn/INSTALL create mode 100644 app/src/main/jni/badvpn/INSTALL-WINDOWS create mode 100644 app/src/main/jni/badvpn/arpprobe/BArpProbe.c create mode 100644 app/src/main/jni/badvpn/arpprobe/BArpProbe.h create mode 100644 app/src/main/jni/badvpn/arpprobe/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/badvpn.7 create mode 100644 app/src/main/jni/badvpn/base/BLog.c create mode 100644 app/src/main/jni/badvpn/base/BLog.h create mode 100644 app/src/main/jni/badvpn/base/BLog_syslog.c create mode 100644 app/src/main/jni/badvpn/base/BLog_syslog.h create mode 100644 app/src/main/jni/badvpn/base/BMutex.h create mode 100644 app/src/main/jni/badvpn/base/BPending.c create mode 100644 app/src/main/jni/badvpn/base/BPending.h create mode 100644 app/src/main/jni/badvpn/base/BPending_list.h create mode 100644 app/src/main/jni/badvpn/base/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/base/DebugObject.c create mode 100644 app/src/main/jni/badvpn/base/DebugObject.h create mode 100644 app/src/main/jni/badvpn/blog_channels.txt create mode 100644 app/src/main/jni/badvpn/blog_generator/blog.php create mode 100644 app/src/main/jni/badvpn/blog_generator/blog_functions.php create mode 100644 app/src/main/jni/badvpn/bproto/BProto.h create mode 100644 app/src/main/jni/badvpn/bproto_generator/ProtoParser.lime create mode 100644 app/src/main/jni/badvpn/bproto_generator/ProtoParser.php create mode 100644 app/src/main/jni/badvpn/bproto_generator/bproto.php create mode 100644 app/src/main/jni/badvpn/bproto_generator/bproto_functions.php create mode 100644 app/src/main/jni/badvpn/client/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/client/DPReceive.c create mode 100644 app/src/main/jni/badvpn/client/DPReceive.h create mode 100644 app/src/main/jni/badvpn/client/DPRelay.c create mode 100644 app/src/main/jni/badvpn/client/DPRelay.h create mode 100644 app/src/main/jni/badvpn/client/DataProto.c create mode 100644 app/src/main/jni/badvpn/client/DataProto.h create mode 100644 app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.c create mode 100644 app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.h create mode 100644 app/src/main/jni/badvpn/client/DatagramPeerIO.c create mode 100644 app/src/main/jni/badvpn/client/DatagramPeerIO.h create mode 100644 app/src/main/jni/badvpn/client/FragmentProtoAssembler.c create mode 100644 app/src/main/jni/badvpn/client/FragmentProtoAssembler.h create mode 100644 app/src/main/jni/badvpn/client/FragmentProtoAssembler_tree.h create mode 100644 app/src/main/jni/badvpn/client/FragmentProtoDisassembler.c create mode 100644 app/src/main/jni/badvpn/client/FragmentProtoDisassembler.h create mode 100644 app/src/main/jni/badvpn/client/FrameDecider.c create mode 100644 app/src/main/jni/badvpn/client/FrameDecider.h create mode 100644 app/src/main/jni/badvpn/client/FrameDecider_groups_tree.h create mode 100644 app/src/main/jni/badvpn/client/FrameDecider_macs_tree.h create mode 100644 app/src/main/jni/badvpn/client/FrameDecider_multicast_tree.h create mode 100644 app/src/main/jni/badvpn/client/PasswordListener.c create mode 100644 app/src/main/jni/badvpn/client/PasswordListener.h create mode 100644 app/src/main/jni/badvpn/client/PeerChat.c create mode 100644 app/src/main/jni/badvpn/client/PeerChat.h create mode 100644 app/src/main/jni/badvpn/client/SCOutmsgEncoder.c create mode 100644 app/src/main/jni/badvpn/client/SCOutmsgEncoder.h create mode 100644 app/src/main/jni/badvpn/client/SPProtoDecoder.c create mode 100644 app/src/main/jni/badvpn/client/SPProtoDecoder.h create mode 100644 app/src/main/jni/badvpn/client/SPProtoEncoder.c create mode 100644 app/src/main/jni/badvpn/client/SPProtoEncoder.h create mode 100644 app/src/main/jni/badvpn/client/SimpleStreamBuffer.c create mode 100644 app/src/main/jni/badvpn/client/SimpleStreamBuffer.h create mode 100644 app/src/main/jni/badvpn/client/SinglePacketSource.c create mode 100644 app/src/main/jni/badvpn/client/SinglePacketSource.h create mode 100644 app/src/main/jni/badvpn/client/StreamPeerIO.c create mode 100644 app/src/main/jni/badvpn/client/StreamPeerIO.h create mode 100644 app/src/main/jni/badvpn/client/badvpn-client.8 create mode 100644 app/src/main/jni/badvpn/client/client.c create mode 100644 app/src/main/jni/badvpn/client/client.h create mode 100644 app/src/main/jni/badvpn/cmake/modules/COPYING-CMAKE-SCRIPTS create mode 100644 app/src/main/jni/badvpn/cmake/modules/FindGLIB2.cmake create mode 100644 app/src/main/jni/badvpn/cmake/modules/FindLibraryWithDebug.cmake create mode 100644 app/src/main/jni/badvpn/cmake/modules/FindNSPR.cmake create mode 100644 app/src/main/jni/badvpn/cmake/modules/FindNSS.cmake create mode 100644 app/src/main/jni/badvpn/cmake/modules/FindOpenSSL.cmake create mode 100644 app/src/main/jni/badvpn/compile-tun2sock.sh create mode 100644 app/src/main/jni/badvpn/dhcpclient/BDHCPClient.c create mode 100644 app/src/main/jni/badvpn/dhcpclient/BDHCPClient.h create mode 100644 app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.c create mode 100644 app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.h create mode 100644 app/src/main/jni/badvpn/dhcpclient/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.c create mode 100644 app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.h create mode 100644 app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.c create mode 100644 app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.h create mode 100644 app/src/main/jni/badvpn/dostest/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/dostest/StreamBuffer.c create mode 100644 app/src/main/jni/badvpn/dostest/StreamBuffer.h create mode 100644 app/src/main/jni/badvpn/dostest/dostest-attacker.c create mode 100644 app/src/main/jni/badvpn/dostest/dostest-server.c create mode 100644 app/src/main/jni/badvpn/examples/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/examples/FastPacketSource.h create mode 100644 app/src/main/jni/badvpn/examples/RandomPacketSink.h create mode 100644 app/src/main/jni/badvpn/examples/TimerPacketSink.h create mode 100644 app/src/main/jni/badvpn/examples/arpprobe_test.c create mode 100644 app/src/main/jni/badvpn/examples/bavl_test.c create mode 100644 app/src/main/jni/badvpn/examples/bencryption_bench.c create mode 100644 app/src/main/jni/badvpn/examples/bprocess_example.c create mode 100644 app/src/main/jni/badvpn/examples/brandom2_test.c create mode 100644 app/src/main/jni/badvpn/examples/btimer_example.c create mode 100644 app/src/main/jni/badvpn/examples/cavl_test.c create mode 100644 app/src/main/jni/badvpn/examples/cavl_test_tree.h create mode 100644 app/src/main/jni/badvpn/examples/dhcpclient_test.c create mode 100644 app/src/main/jni/badvpn/examples/emscripten_test.c create mode 100644 app/src/main/jni/badvpn/examples/fairqueue_test.c create mode 100644 app/src/main/jni/badvpn/examples/fairqueue_test2.c create mode 100644 app/src/main/jni/badvpn/examples/indexedlist_test.c create mode 100644 app/src/main/jni/badvpn/examples/ipaddr6_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncd_parser_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncd_tokenizer_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncd_value_parser_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncdinterfacemonitor_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncdudevmanager_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncdudevmonitor_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncdval_test.c create mode 100644 app/src/main/jni/badvpn/examples/ncdvalcons_test.c create mode 100644 app/src/main/jni/badvpn/examples/parse_number_test.c create mode 100644 app/src/main/jni/badvpn/examples/predicate_test.c create mode 100644 app/src/main/jni/badvpn/examples/savl_test.c create mode 100644 app/src/main/jni/badvpn/examples/savl_test_tree.h create mode 100644 app/src/main/jni/badvpn/examples/stdin_input.c create mode 100644 app/src/main/jni/badvpn/examples/substring_test.c create mode 100644 app/src/main/jni/badvpn/fix_flex.php create mode 100644 app/src/main/jni/badvpn/flooder/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/flooder/flooder.c create mode 100644 app/src/main/jni/badvpn/flooder/flooder.h create mode 100644 app/src/main/jni/badvpn/flow/BufferWriter.c create mode 100644 app/src/main/jni/badvpn/flow/BufferWriter.h create mode 100644 app/src/main/jni/badvpn/flow/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/flow/LineBuffer.c create mode 100644 app/src/main/jni/badvpn/flow/LineBuffer.h create mode 100644 app/src/main/jni/badvpn/flow/PacketBuffer.c create mode 100644 app/src/main/jni/badvpn/flow/PacketBuffer.h create mode 100644 app/src/main/jni/badvpn/flow/PacketCopier.c create mode 100644 app/src/main/jni/badvpn/flow/PacketCopier.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassConnector.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassConnector.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassFairQueue.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassFairQueue.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassFifoQueue.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassFifoQueue.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassInterface.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassInterface.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassNotifier.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassNotifier.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.c create mode 100644 app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.h create mode 100644 app/src/main/jni/badvpn/flow/PacketPassPriorityQueue_tree.h create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoDecoder.c create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoDecoder.h create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoEncoder.c create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoEncoder.h create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoFlow.c create mode 100644 app/src/main/jni/badvpn/flow/PacketProtoFlow.h create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvBlocker.c create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvBlocker.h create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvConnector.c create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvConnector.h create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvInterface.c create mode 100644 app/src/main/jni/badvpn/flow/PacketRecvInterface.h create mode 100644 app/src/main/jni/badvpn/flow/PacketRouter.c create mode 100644 app/src/main/jni/badvpn/flow/PacketRouter.h create mode 100644 app/src/main/jni/badvpn/flow/PacketStreamSender.c create mode 100644 app/src/main/jni/badvpn/flow/PacketStreamSender.h create mode 100644 app/src/main/jni/badvpn/flow/RouteBuffer.c create mode 100644 app/src/main/jni/badvpn/flow/RouteBuffer.h create mode 100644 app/src/main/jni/badvpn/flow/SinglePacketBuffer.c create mode 100644 app/src/main/jni/badvpn/flow/SinglePacketBuffer.h create mode 100644 app/src/main/jni/badvpn/flow/SinglePacketSender.c create mode 100644 app/src/main/jni/badvpn/flow/SinglePacketSender.h create mode 100644 app/src/main/jni/badvpn/flow/SingleStreamReceiver.c create mode 100644 app/src/main/jni/badvpn/flow/SingleStreamReceiver.h create mode 100644 app/src/main/jni/badvpn/flow/SingleStreamSender.c create mode 100644 app/src/main/jni/badvpn/flow/SingleStreamSender.h create mode 100644 app/src/main/jni/badvpn/flow/StreamPacketSender.c create mode 100644 app/src/main/jni/badvpn/flow/StreamPacketSender.h create mode 100644 app/src/main/jni/badvpn/flow/StreamPassConnector.c create mode 100644 app/src/main/jni/badvpn/flow/StreamPassConnector.h create mode 100644 app/src/main/jni/badvpn/flow/StreamPassInterface.c create mode 100644 app/src/main/jni/badvpn/flow/StreamPassInterface.h create mode 100644 app/src/main/jni/badvpn/flow/StreamRecvConnector.c create mode 100644 app/src/main/jni/badvpn/flow/StreamRecvConnector.h create mode 100644 app/src/main/jni/badvpn/flow/StreamRecvInterface.c create mode 100644 app/src/main/jni/badvpn/flow/StreamRecvInterface.h create mode 100644 app/src/main/jni/badvpn/flowextra/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/flowextra/KeepaliveIO.c create mode 100644 app/src/main/jni/badvpn/flowextra/KeepaliveIO.h create mode 100644 app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c create mode 100644 app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h create mode 100644 app/src/main/jni/badvpn/generate_files create mode 100644 app/src/main/jni/badvpn/generated/NCDConfigParser_parse.c create mode 100644 app/src/main/jni/badvpn/generated/NCDConfigParser_parse.h create mode 100644 app/src/main/jni/badvpn/generated/NCDConfigParser_parse.out create mode 100644 app/src/main/jni/badvpn/generated/NCDConfigParser_parse.y create mode 100644 app/src/main/jni/badvpn/generated/NCDValParser_parse.c create mode 100644 app/src/main/jni/badvpn/generated/NCDValParser_parse.h create mode 100644 app/src/main/jni/badvpn/generated/NCDValParser_parse.out create mode 100644 app/src/main/jni/badvpn/generated/NCDValParser_parse.y create mode 100644 app/src/main/jni/badvpn/generated/bison_BPredicate.c create mode 100644 app/src/main/jni/badvpn/generated/bison_BPredicate.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BArpProbe.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BConnection.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BDHCPClient.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BDHCPClientCore.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BDatagram.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BEncryption.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BInputProcess.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BLockReactor.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BNetwork.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BPredicate.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BProcess.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BReactor.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BSSLConnection.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BSignal.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BTap.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BThreadSignal.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BThreadWork.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BTime.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_DPReceive.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_DPRelay.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_DataProto.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_DatagramPeerIO.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_FragmentProtoAssembler.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_FrameDecider.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_LineBuffer.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_Listener.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDBuildProgram.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDConfigParser.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDConfigTokenizer.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDIfConfig.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDInterfaceMonitor.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDModuleIndex.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDModuleProcess.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDPlaceholderDb.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDRequest.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDRequestClient.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDRfkillMonitor.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDUdevCache.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDUdevManager.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitor.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitorParser.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDVal.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDValGenerator.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_NCDValParser.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_PRStreamSink.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_PRStreamSource.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_PasswordListener.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_PeerChat.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_SPProtoDecoder.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ServerConnection.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_StreamPeerIO.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_addr.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_client.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_dostest_attacker.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_dostest_server.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_flooder.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_lwip.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_alias.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_arithmetic.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_assert.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_backtrack.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_blocker.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_buffer.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_call2.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_choose.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_concat.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_daemon.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_depend.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_depend_scope.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_dynamic_depend.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_exit.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_explode.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_file.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_file_open.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_foreach.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_from_string.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_getargs.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_getenv.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_if.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_imperative.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_implode.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_index.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_list.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_load_module.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_log.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_log_msg.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_logical.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_multidepend.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_badvpn.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_rfkill.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitdevice.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitlink.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_wpa_supplicant.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_dns.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_iptables.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr_in_network.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_arp_probe.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_dhcp.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_route.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr_in_network.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_route.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_up.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_net_watch_interfaces.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_netmask.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_ondemand.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_parse.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_print.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_process_manager.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_reboot.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_ref.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_regex_match.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_request.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_run.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_runonce.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sleep.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_socket.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_spawn.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_strcmp.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_substr.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_evdev.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_client.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_server.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_start_process.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_directory.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_input.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_usb.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_timer.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_to_string.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_try.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_value.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_valuemetic.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_ncd_var.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_nsskey.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_server.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_tun2socks.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channel_udpgw.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channels_defines.h create mode 100644 app/src/main/jni/badvpn/generated/blog_channels_list.h create mode 100644 app/src/main/jni/badvpn/generated/bproto_addr.h create mode 100644 app/src/main/jni/badvpn/generated/bproto_bproto_test.h create mode 100644 app/src/main/jni/badvpn/generated/bproto_msgproto.h create mode 100644 app/src/main/jni/badvpn/generated/flex_BPredicate.c create mode 100644 app/src/main/jni/badvpn/generated/flex_BPredicate.h create mode 100644 app/src/main/jni/badvpn/lemon/lemon.c create mode 100644 app/src/main/jni/badvpn/lemon/lempar.c create mode 100644 app/src/main/jni/badvpn/lime/HOWTO create mode 100644 app/src/main/jni/badvpn/lime/flex_token_stream.php create mode 100644 app/src/main/jni/badvpn/lime/lemon.c create mode 100644 app/src/main/jni/badvpn/lime/lime.bootstrap create mode 100644 app/src/main/jni/badvpn/lime/lime.php create mode 100644 app/src/main/jni/badvpn/lime/lime_scan_tokens.l create mode 100644 app/src/main/jni/badvpn/lime/metagrammar create mode 100644 app/src/main/jni/badvpn/lime/parse_engine.php create mode 100644 app/src/main/jni/badvpn/lime/set.so.php create mode 100644 app/src/main/jni/badvpn/lwip/CHANGELOG create mode 100644 app/src/main/jni/badvpn/lwip/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/lwip/COPYING create mode 100644 app/src/main/jni/badvpn/lwip/FILES create mode 100644 app/src/main/jni/badvpn/lwip/README create mode 100644 app/src/main/jni/badvpn/lwip/UPGRADING create mode 100644 app/src/main/jni/badvpn/lwip/custom/arch/cc.h create mode 100644 app/src/main/jni/badvpn/lwip/custom/arch/perf.h create mode 100644 app/src/main/jni/badvpn/lwip/custom/lwipopts.h create mode 100644 app/src/main/jni/badvpn/lwip/custom/sys.c create mode 100644 app/src/main/jni/badvpn/lwip/doc/FILES create mode 100644 app/src/main/jni/badvpn/lwip/doc/contrib.txt create mode 100644 app/src/main/jni/badvpn/lwip/doc/rawapi.txt create mode 100644 app/src/main/jni/badvpn/lwip/doc/savannah.txt create mode 100644 app/src/main/jni/badvpn/lwip/doc/snmp_agent.txt create mode 100644 app/src/main/jni/badvpn/lwip/doc/sys_arch.txt create mode 100644 app/src/main/jni/badvpn/lwip/lwip-base-version create mode 100644 app/src/main/jni/badvpn/lwip/src/FILES create mode 100644 app/src/main/jni/badvpn/lwip/src/api/api_lib.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/api_msg.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/err.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/netbuf.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/netdb.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/netifapi.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/sockets.c create mode 100644 app/src/main/jni/badvpn/lwip/src/api/tcpip.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/def.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/dhcp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/dns.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/inet_chksum.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/init.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/README create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/mem.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/memp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/netif.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/pbuf.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/raw.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_dec.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_enc.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/mib2.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/mib_structs.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/msg_in.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/snmp/msg_out.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/stats.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/sys.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/tcp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/tcp_in.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/tcp_out.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/timers.c create mode 100644 app/src/main/jni/badvpn/lwip/src/core/udp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/api.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/arch.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/debug.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/def.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/dns.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/err.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/init.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/ip.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/mem.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/memp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/netdb.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/netif.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/opt.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/raw.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/sio.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_asn1.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/stats.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/sys.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/timers.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/lwip/udp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/netif/etharp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/netif/slipif.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/posix/netdb.h create mode 100644 app/src/main/jni/badvpn/lwip/src/include/posix/sys/socket.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/FILES create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/etharp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ethernetif.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_impl.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_oe.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/pppdebug.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/readme.txt create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.c create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.h create mode 100644 app/src/main/jni/badvpn/lwip/src/netif/slipif.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/lwip_check.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/lwip_unittests.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/lwipopts.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.h create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.c create mode 100644 app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.h create mode 100644 app/src/main/jni/badvpn/misc/BRefTarget.h create mode 100644 app/src/main/jni/badvpn/misc/Utf16Decoder.h create mode 100644 app/src/main/jni/badvpn/misc/Utf16Encoder.h create mode 100644 app/src/main/jni/badvpn/misc/Utf8Decoder.h create mode 100644 app/src/main/jni/badvpn/misc/Utf8Encoder.h create mode 100644 app/src/main/jni/badvpn/misc/arp_proto.h create mode 100644 app/src/main/jni/badvpn/misc/array_length.h create mode 100644 app/src/main/jni/badvpn/misc/balign.h create mode 100644 app/src/main/jni/badvpn/misc/balloc.h create mode 100644 app/src/main/jni/badvpn/misc/blimits.h create mode 100644 app/src/main/jni/badvpn/misc/bsize.h create mode 100644 app/src/main/jni/badvpn/misc/bsort.h create mode 100644 app/src/main/jni/badvpn/misc/bstring.h create mode 100644 app/src/main/jni/badvpn/misc/byteorder.h create mode 100644 app/src/main/jni/badvpn/misc/cmdline.h create mode 100644 app/src/main/jni/badvpn/misc/compare.h create mode 100644 app/src/main/jni/badvpn/misc/concat_strings.h create mode 100644 app/src/main/jni/badvpn/misc/cstring.h create mode 100644 app/src/main/jni/badvpn/misc/dead.h create mode 100644 app/src/main/jni/badvpn/misc/debug.h create mode 100644 app/src/main/jni/badvpn/misc/debugcounter.h create mode 100644 app/src/main/jni/badvpn/misc/debugerror.h create mode 100644 app/src/main/jni/badvpn/misc/dhcp_proto.h create mode 100644 app/src/main/jni/badvpn/misc/ethernet_proto.h create mode 100644 app/src/main/jni/badvpn/misc/exparray.h create mode 100644 app/src/main/jni/badvpn/misc/expstring.h create mode 100644 app/src/main/jni/badvpn/misc/find_char.h create mode 100644 app/src/main/jni/badvpn/misc/find_program.h create mode 100644 app/src/main/jni/badvpn/misc/get_iface_info.h create mode 100644 app/src/main/jni/badvpn/misc/grow_array.h create mode 100644 app/src/main/jni/badvpn/misc/hashfun.h create mode 100644 app/src/main/jni/badvpn/misc/igmp_proto.h create mode 100644 app/src/main/jni/badvpn/misc/ipaddr.h create mode 100644 app/src/main/jni/badvpn/misc/ipaddr6.h create mode 100644 app/src/main/jni/badvpn/misc/ipv4_proto.h create mode 100644 app/src/main/jni/badvpn/misc/ipv6_proto.h create mode 100644 app/src/main/jni/badvpn/misc/loggers_string.h create mode 100644 app/src/main/jni/badvpn/misc/loglevel.h create mode 100644 app/src/main/jni/badvpn/misc/maxalign.h create mode 100644 app/src/main/jni/badvpn/misc/merge.h create mode 100644 app/src/main/jni/badvpn/misc/minmax.h create mode 100644 app/src/main/jni/badvpn/misc/modadd.h create mode 100644 app/src/main/jni/badvpn/misc/mswsock.h create mode 100644 app/src/main/jni/badvpn/misc/nonblocking.h create mode 100644 app/src/main/jni/badvpn/misc/nsskey.h create mode 100644 app/src/main/jni/badvpn/misc/offset.h create mode 100644 app/src/main/jni/badvpn/misc/open_standard_streams.h create mode 100644 app/src/main/jni/badvpn/misc/overflow.h create mode 100644 app/src/main/jni/badvpn/misc/packed.h create mode 100644 app/src/main/jni/badvpn/misc/parse_number.h create mode 100644 app/src/main/jni/badvpn/misc/print_macros.h create mode 100644 app/src/main/jni/badvpn/misc/read_file.h create mode 100644 app/src/main/jni/badvpn/misc/read_write_int.h create mode 100644 app/src/main/jni/badvpn/misc/socks_proto.h create mode 100644 app/src/main/jni/badvpn/misc/sslsocket.h create mode 100644 app/src/main/jni/badvpn/misc/stdbuf_cmdline.h create mode 100644 app/src/main/jni/badvpn/misc/strdup.h create mode 100644 app/src/main/jni/badvpn/misc/string_begins_with.h create mode 100644 app/src/main/jni/badvpn/misc/substring.h create mode 100644 app/src/main/jni/badvpn/misc/udp_proto.h create mode 100644 app/src/main/jni/badvpn/misc/unicode_funcs.h create mode 100644 app/src/main/jni/badvpn/misc/version.h create mode 100644 app/src/main/jni/badvpn/misc/write_file.h create mode 100644 app/src/main/jni/badvpn/ncd-request/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/ncd-request/ncd-request.c create mode 100644 app/src/main/jni/badvpn/ncd/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/ncd/NCDAst.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDAst.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDBuildProgram.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDBuildProgram.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDConfigParser.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDConfigParser.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDConfigParser_parse.y create mode 100644 app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpProcess.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpProcess.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpProg.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpProg.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpProg_hash.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpreter.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDInterpreter.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDMethodIndex.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDMethodIndex.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDMethodIndex_hash.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDModule.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDModule.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDModuleIndex.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDModuleIndex.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDModuleIndex_mhash.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDObject.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDObject.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDStringIndex.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDStringIndex.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDStringIndex_hash.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDSugar.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDSugar.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDVal.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDVal.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDValCons.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDValCons.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDValGenerator.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDValGenerator.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDValParser.c create mode 100644 app/src/main/jni/badvpn/ncd/NCDValParser.h create mode 100644 app/src/main/jni/badvpn/ncd/NCDValParser_parse.y create mode 100644 app/src/main/jni/badvpn/ncd/NCDVal_maptree.h create mode 100644 app/src/main/jni/badvpn/ncd/README create mode 100644 app/src/main/jni/badvpn/ncd/emncd.c create mode 100644 app/src/main/jni/badvpn/ncd/emncd.html create mode 100644 app/src/main/jni/badvpn/ncd/examples/dbus_start.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/dhcpd.conf.template create mode 100644 app/src/main/jni/badvpn/ncd/examples/directory_updater.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/events.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/igmpproxy.conf.template create mode 100644 app/src/main/jni/badvpn/ncd/examples/make_dhcp_config.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/make_igmpproxy_config.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/network.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/onoff_server.ncdi create mode 100644 app/src/main/jni/badvpn/ncd/examples/onoff_server_test.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/tcp_echo_client.ncd create mode 100644 app/src/main/jni/badvpn/ncd/examples/tcp_echo_server.ncd create mode 100644 app/src/main/jni/badvpn/ncd/extra/BEventLock.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/BEventLock.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDBuf.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDBuf.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/address_utils.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/build_cmdline.c create mode 100644 app/src/main/jni/badvpn/ncd/extra/build_cmdline.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/make_fast_names.h create mode 100644 app/src/main/jni/badvpn/ncd/extra/value_utils.h create mode 100644 app/src/main/jni/badvpn/ncd/include_linux_input.c create mode 100644 app/src/main/jni/badvpn/ncd/make_name_indices.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/alias.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/arithmetic.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/assert.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/backtrack.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/blocker.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/buffer.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/buffer_chunks_tree.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/call2.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/choose.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/command_template.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/command_template.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/concat.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/daemon.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/depend.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/depend_scope.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/dynamic_depend.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/event_template.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/event_template.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/exit.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/explode.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/file.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/file_open.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/foreach.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/from_string.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/getargs.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/getenv.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/if.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/imperative.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/implode.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/index.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/list.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/load_module.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/log.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/logical.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/modules.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/multidepend.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_backend_badvpn.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_backend_rfkill.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_backend_waitdevice.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_backend_waitlink.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_backend_wpa_supplicant.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_dns.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_iptables.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr_in_network.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv4_arp_probe.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv4_dhcp.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv4_route.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr_in_network.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv6_route.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_ipv6_wait_dynamic_addr.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_up.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/net_watch_interfaces.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/netmask.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/ondemand.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/parse.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/print.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/process_manager.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/reboot.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/ref.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/regex_match.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/run.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/runonce.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sleep.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/socket.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/spawn.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/strcmp.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/substr.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_evdev.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_request_client.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_request_server.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_start_process.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_watch_directory.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_watch_input.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/sys_watch_usb.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/timer.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/to_string.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/try.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/value.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/value_maptree.h create mode 100644 app/src/main/jni/badvpn/ncd/modules/valuemetic.c create mode 100644 app/src/main/jni/badvpn/ncd/modules/var.c create mode 100644 app/src/main/jni/badvpn/ncd/ncd.c create mode 100644 app/src/main/jni/badvpn/ncd/ncd.h create mode 100644 app/src/main/jni/badvpn/ncd/parse_linux_input.sh create mode 100644 app/src/main/jni/badvpn/ncd/static_strings.h create mode 100644 app/src/main/jni/badvpn/ncd/tests/addr_in_network.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/alias.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/arithmetic.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/backtracking.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/buffer.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/call.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/concat.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/depend.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/depend_scope.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/escape_and_nulls.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/explode.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/foreach.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/if.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/implode.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/include.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/include_included.ncdi create mode 100644 app/src/main/jni/badvpn/ncd/tests/include_included2.ncdi create mode 100644 app/src/main/jni/badvpn/ncd/tests/logical.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/multidepend.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/netmask.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/parse.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/process_manager.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/regex.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/run_tests create mode 100644 app/src/main/jni/badvpn/ncd/tests/strings.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/substr.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/turing.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/value.ncd create mode 100644 app/src/main/jni/badvpn/ncd/tests/value_substr.ncd create mode 100644 app/src/main/jni/badvpn/nspr_support/BSSLConnection.c create mode 100644 app/src/main/jni/badvpn/nspr_support/BSSLConnection.h create mode 100644 app/src/main/jni/badvpn/nspr_support/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.c create mode 100644 app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.h create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate.c create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate.h create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate.l create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate.y create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate_internal.h create mode 100644 app/src/main/jni/badvpn/predicate/BPredicate_parser.h create mode 100644 app/src/main/jni/badvpn/predicate/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/predicate/LexMemoryBufferInput.h create mode 100644 app/src/main/jni/badvpn/protocol/addr.bproto create mode 100644 app/src/main/jni/badvpn/protocol/addr.h create mode 100644 app/src/main/jni/badvpn/protocol/dataproto.h create mode 100644 app/src/main/jni/badvpn/protocol/fragmentproto.h create mode 100644 app/src/main/jni/badvpn/protocol/msgproto.bproto create mode 100644 app/src/main/jni/badvpn/protocol/msgproto.h create mode 100644 app/src/main/jni/badvpn/protocol/packetproto.h create mode 100644 app/src/main/jni/badvpn/protocol/requestproto.h create mode 100644 app/src/main/jni/badvpn/protocol/scproto.h create mode 100644 app/src/main/jni/badvpn/protocol/spproto.h create mode 100644 app/src/main/jni/badvpn/protocol/udpgw_proto.h create mode 100644 app/src/main/jni/badvpn/random/BRandom2.c create mode 100644 app/src/main/jni/badvpn/random/BRandom2.h create mode 100644 app/src/main/jni/badvpn/random/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/scripts/cmake create mode 100644 app/src/main/jni/badvpn/scripts/copy_nss create mode 100644 app/src/main/jni/badvpn/scripts/toolchain.cmake create mode 100644 app/src/main/jni/badvpn/security/BEncryption.c create mode 100644 app/src/main/jni/badvpn/security/BEncryption.h create mode 100644 app/src/main/jni/badvpn/security/BHash.c create mode 100644 app/src/main/jni/badvpn/security/BHash.h create mode 100644 app/src/main/jni/badvpn/security/BRandom.c create mode 100644 app/src/main/jni/badvpn/security/BRandom.h create mode 100644 app/src/main/jni/badvpn/security/BSecurity.c create mode 100644 app/src/main/jni/badvpn/security/BSecurity.h create mode 100644 app/src/main/jni/badvpn/security/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/security/OTPCalculator.c create mode 100644 app/src/main/jni/badvpn/security/OTPCalculator.h create mode 100644 app/src/main/jni/badvpn/security/OTPChecker.c create mode 100644 app/src/main/jni/badvpn/security/OTPChecker.h create mode 100644 app/src/main/jni/badvpn/security/OTPGenerator.c create mode 100644 app/src/main/jni/badvpn/security/OTPGenerator.h create mode 100644 app/src/main/jni/badvpn/server/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/server/badvpn-server.8 create mode 100644 app/src/main/jni/badvpn/server/server.c create mode 100644 app/src/main/jni/badvpn/server/server.h create mode 100644 app/src/main/jni/badvpn/server_connection/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.c create mode 100644 app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.h create mode 100644 app/src/main/jni/badvpn/server_connection/ServerConnection.c create mode 100644 app/src/main/jni/badvpn/server_connection/ServerConnection.h create mode 100644 app/src/main/jni/badvpn/socksclient/BSocksClient.c create mode 100644 app/src/main/jni/badvpn/socksclient/BSocksClient.h create mode 100644 app/src/main/jni/badvpn/socksclient/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/stringmap/BStringMap.c create mode 100644 app/src/main/jni/badvpn/stringmap/BStringMap.h create mode 100644 app/src/main/jni/badvpn/stringmap/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/structure/BAVL.h create mode 100644 app/src/main/jni/badvpn/structure/CAvl.h create mode 100644 app/src/main/jni/badvpn/structure/CAvl_decl.h create mode 100644 app/src/main/jni/badvpn/structure/CAvl_footer.h create mode 100644 app/src/main/jni/badvpn/structure/CAvl_header.h create mode 100644 app/src/main/jni/badvpn/structure/CAvl_impl.h create mode 100644 app/src/main/jni/badvpn/structure/CHash.h create mode 100644 app/src/main/jni/badvpn/structure/CHash_decl.h create mode 100644 app/src/main/jni/badvpn/structure/CHash_footer.h create mode 100644 app/src/main/jni/badvpn/structure/CHash_header.h create mode 100644 app/src/main/jni/badvpn/structure/CHash_impl.h create mode 100644 app/src/main/jni/badvpn/structure/ChunkBuffer2.h create mode 100644 app/src/main/jni/badvpn/structure/IndexedList.h create mode 100644 app/src/main/jni/badvpn/structure/IndexedList_tree.h create mode 100644 app/src/main/jni/badvpn/structure/LinkedList0.h create mode 100644 app/src/main/jni/badvpn/structure/LinkedList1.h create mode 100644 app/src/main/jni/badvpn/structure/LinkedList3.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl_decl.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl_footer.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl_header.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl_impl.h create mode 100644 app/src/main/jni/badvpn/structure/SAvl_tree.h create mode 100644 app/src/main/jni/badvpn/structure/SLinkedList.h create mode 100644 app/src/main/jni/badvpn/structure/SLinkedList_decl.h create mode 100644 app/src/main/jni/badvpn/structure/SLinkedList_footer.h create mode 100644 app/src/main/jni/badvpn/structure/SLinkedList_header.h create mode 100644 app/src/main/jni/badvpn/structure/SLinkedList_impl.h create mode 100644 app/src/main/jni/badvpn/system/BAddr.h create mode 100644 app/src/main/jni/badvpn/system/BConnection.h create mode 100644 app/src/main/jni/badvpn/system/BConnectionGeneric.h create mode 100644 app/src/main/jni/badvpn/system/BConnection_unix.c create mode 100644 app/src/main/jni/badvpn/system/BConnection_unix.h create mode 100644 app/src/main/jni/badvpn/system/BConnection_win.c create mode 100644 app/src/main/jni/badvpn/system/BConnection_win.h create mode 100644 app/src/main/jni/badvpn/system/BDatagram.h create mode 100644 app/src/main/jni/badvpn/system/BDatagram_unix.c create mode 100644 app/src/main/jni/badvpn/system/BDatagram_unix.h create mode 100644 app/src/main/jni/badvpn/system/BDatagram_win.c create mode 100644 app/src/main/jni/badvpn/system/BDatagram_win.h create mode 100644 app/src/main/jni/badvpn/system/BInputProcess.c create mode 100644 app/src/main/jni/badvpn/system/BInputProcess.h create mode 100644 app/src/main/jni/badvpn/system/BLockReactor.c create mode 100644 app/src/main/jni/badvpn/system/BLockReactor.h create mode 100644 app/src/main/jni/badvpn/system/BNetwork.c create mode 100644 app/src/main/jni/badvpn/system/BNetwork.h create mode 100644 app/src/main/jni/badvpn/system/BProcess.c create mode 100644 app/src/main/jni/badvpn/system/BProcess.h create mode 100644 app/src/main/jni/badvpn/system/BReactor.h create mode 100644 app/src/main/jni/badvpn/system/BReactor_badvpn.c create mode 100644 app/src/main/jni/badvpn/system/BReactor_badvpn.h create mode 100644 app/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h create mode 100644 app/src/main/jni/badvpn/system/BReactor_emscripten.c create mode 100644 app/src/main/jni/badvpn/system/BReactor_emscripten.h create mode 100644 app/src/main/jni/badvpn/system/BReactor_glib.c create mode 100644 app/src/main/jni/badvpn/system/BReactor_glib.h create mode 100644 app/src/main/jni/badvpn/system/BSignal.c create mode 100644 app/src/main/jni/badvpn/system/BSignal.h create mode 100644 app/src/main/jni/badvpn/system/BThreadSignal.c create mode 100644 app/src/main/jni/badvpn/system/BThreadSignal.h create mode 100644 app/src/main/jni/badvpn/system/BTime.c create mode 100644 app/src/main/jni/badvpn/system/BTime.h create mode 100644 app/src/main/jni/badvpn/system/BUnixSignal.c create mode 100644 app/src/main/jni/badvpn/system/BUnixSignal.h create mode 100644 app/src/main/jni/badvpn/system/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tests/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tests/bproto_test.bproto create mode 100644 app/src/main/jni/badvpn/tests/bproto_test.c create mode 100644 app/src/main/jni/badvpn/tests/chunkbuffer2_test.c create mode 100644 app/src/main/jni/badvpn/tests/threadwork_test.c create mode 100644 app/src/main/jni/badvpn/threadwork/BThreadWork.c create mode 100644 app/src/main/jni/badvpn/threadwork/BThreadWork.h create mode 100644 app/src/main/jni/badvpn/threadwork/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tun2socks/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c create mode 100644 app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h create mode 100644 app/src/main/jni/badvpn/tun2socks/badvpn-tun2socks.8 create mode 100644 app/src/main/jni/badvpn/tun2socks/tun2socks.c create mode 100644 app/src/main/jni/badvpn/tun2socks/tun2socks.h create mode 100644 app/src/main/jni/badvpn/tunctl/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tunctl/tunctl.c create mode 100644 app/src/main/jni/badvpn/tuntap/BTap.c create mode 100644 app/src/main/jni/badvpn/tuntap/BTap.h create mode 100644 app/src/main/jni/badvpn/tuntap/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/tuntap/tapwin32-funcs.c create mode 100644 app/src/main/jni/badvpn/tuntap/tapwin32-funcs.h create mode 100644 app/src/main/jni/badvpn/tuntap/wintap-common.h create mode 100644 app/src/main/jni/badvpn/udevmonitor/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.c create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.h create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.c create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.h create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.c create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.h create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.c create mode 100644 app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.h create mode 100644 app/src/main/jni/badvpn/udpgw/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/udpgw/udpgw.c create mode 100644 app/src/main/jni/badvpn/udpgw/udpgw.h create mode 100644 app/src/main/jni/badvpn/udpgw_client/CMakeLists.txt create mode 100644 app/src/main/jni/badvpn/udpgw_client/UdpGwClient.c create mode 100644 app/src/main/jni/badvpn/udpgw_client/UdpGwClient.h create mode 100644 app/src/main/jni/libancillary/API create mode 100644 app/src/main/jni/libancillary/COPYING create mode 100644 app/src/main/jni/libancillary/Makefile create mode 100644 app/src/main/jni/libancillary/ancillary.h create mode 100644 app/src/main/jni/libancillary/fd_recv.c create mode 100644 app/src/main/jni/libancillary/fd_send.c create mode 100644 app/src/main/jni/libancillary/test.c create mode 100644 app/src/main/jni/pdnsd/AUTHORS create mode 100644 app/src/main/jni/pdnsd/COPYING create mode 100644 app/src/main/jni/pdnsd/COPYING.BSD create mode 100644 app/src/main/jni/pdnsd/ChangeLog create mode 100644 app/src/main/jni/pdnsd/ChangeLog.old create mode 100644 app/src/main/jni/pdnsd/INSTALL create mode 100644 app/src/main/jni/pdnsd/Makefile.am create mode 100644 app/src/main/jni/pdnsd/Makefile.in create mode 100644 app/src/main/jni/pdnsd/NEWS create mode 100644 app/src/main/jni/pdnsd/PKGBUILD.in create mode 100644 app/src/main/jni/pdnsd/README create mode 100644 app/src/main/jni/pdnsd/README.par create mode 100644 app/src/main/jni/pdnsd/README.par.old create mode 100644 app/src/main/jni/pdnsd/THANKS create mode 100644 app/src/main/jni/pdnsd/TODO create mode 100644 app/src/main/jni/pdnsd/acconfig.h create mode 100644 app/src/main/jni/pdnsd/aclocal.m4 create mode 100644 app/src/main/jni/pdnsd/compile create mode 100644 app/src/main/jni/pdnsd/config.h create mode 100644 app/src/main/jni/pdnsd/config.h.in create mode 100644 app/src/main/jni/pdnsd/configure create mode 100644 app/src/main/jni/pdnsd/configure.in create mode 100644 app/src/main/jni/pdnsd/contrib/Makefile.am create mode 100644 app/src/main/jni/pdnsd/contrib/Makefile.in create mode 100644 app/src/main/jni/pdnsd/contrib/README create mode 100644 app/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl create mode 100644 app/src/main/jni/pdnsd/contrib/dhcp2pdnsd create mode 100644 app/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl create mode 100644 app/src/main/jni/pdnsd/depcomp create mode 100644 app/src/main/jni/pdnsd/doc/Makefile.am create mode 100644 app/src/main/jni/pdnsd/doc/Makefile.in create mode 100644 app/src/main/jni/pdnsd/doc/doc_makefile create mode 100644 app/src/main/jni/pdnsd/doc/html/dl.html create mode 100644 app/src/main/jni/pdnsd/doc/html/dl.html.in create mode 100644 app/src/main/jni/pdnsd/doc/html/doc.html create mode 100644 app/src/main/jni/pdnsd/doc/html/doc_makefile create mode 100644 app/src/main/jni/pdnsd/doc/html/faq.html create mode 100644 app/src/main/jni/pdnsd/doc/html/htmlsubst.pl create mode 100644 app/src/main/jni/pdnsd/doc/html/index.html create mode 100644 app/src/main/jni/pdnsd/doc/html2confman.pl create mode 100644 app/src/main/jni/pdnsd/doc/pdnsd-ctl.8 create mode 100644 app/src/main/jni/pdnsd/doc/pdnsd.8.in create mode 100644 app/src/main/jni/pdnsd/doc/pdnsd.conf.5.in create mode 100644 app/src/main/jni/pdnsd/doc/pdnsd.conf.in create mode 100644 app/src/main/jni/pdnsd/doc/txt/doc_makefile create mode 100644 app/src/main/jni/pdnsd/doc/txt/faq.txt create mode 100644 app/src/main/jni/pdnsd/doc/txt/intro.txt create mode 100644 app/src/main/jni/pdnsd/doc/txt/manual.txt create mode 100644 app/src/main/jni/pdnsd/file-list.base.in create mode 100644 app/src/main/jni/pdnsd/install-sh create mode 100644 app/src/main/jni/pdnsd/missing create mode 100644 app/src/main/jni/pdnsd/pdnsd.spec.in create mode 100644 app/src/main/jni/pdnsd/src/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/cache.c create mode 100644 app/src/main/jni/pdnsd/src/cache.h create mode 100644 app/src/main/jni/pdnsd/src/conf-keywords.h create mode 100644 app/src/main/jni/pdnsd/src/conf-parser.c create mode 100644 app/src/main/jni/pdnsd/src/conf-parser.h create mode 100644 app/src/main/jni/pdnsd/src/conff.c create mode 100644 app/src/main/jni/pdnsd/src/conff.h create mode 100644 app/src/main/jni/pdnsd/src/consts.c create mode 100644 app/src/main/jni/pdnsd/src/consts.h create mode 100644 app/src/main/jni/pdnsd/src/debug.c create mode 100644 app/src/main/jni/pdnsd/src/debug.h create mode 100644 app/src/main/jni/pdnsd/src/dns.c create mode 100644 app/src/main/jni/pdnsd/src/dns.h create mode 100644 app/src/main/jni/pdnsd/src/dns_answer.c create mode 100644 app/src/main/jni/pdnsd/src/dns_answer.h create mode 100644 app/src/main/jni/pdnsd/src/dns_query.c create mode 100644 app/src/main/jni/pdnsd/src/dns_query.h create mode 100644 app/src/main/jni/pdnsd/src/error.c create mode 100644 app/src/main/jni/pdnsd/src/error.h create mode 100644 app/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h create mode 100644 app/src/main/jni/pdnsd/src/hash.c create mode 100644 app/src/main/jni/pdnsd/src/hash.h create mode 100644 app/src/main/jni/pdnsd/src/helpers.c create mode 100644 app/src/main/jni/pdnsd/src/helpers.h create mode 100644 app/src/main/jni/pdnsd/src/icmp.c create mode 100644 app/src/main/jni/pdnsd/src/icmp.h create mode 100644 app/src/main/jni/pdnsd/src/ipvers.h create mode 100644 app/src/main/jni/pdnsd/src/list.c create mode 100644 app/src/main/jni/pdnsd/src/list.h create mode 100644 app/src/main/jni/pdnsd/src/main.c create mode 100644 app/src/main/jni/pdnsd/src/make_rr_types_h.pl create mode 100644 app/src/main/jni/pdnsd/src/netdev.c create mode 100644 app/src/main/jni/pdnsd/src/netdev.h create mode 100644 app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c create mode 100644 app/src/main/jni/pdnsd/src/pdnsd_assert.h create mode 100644 app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in create mode 100644 app/src/main/jni/pdnsd/src/rc/Debian/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/Debian/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in create mode 100644 app/src/main/jni/pdnsd/src/rc/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/README create mode 100644 app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in create mode 100644 app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in create mode 100644 app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in create mode 100644 app/src/main/jni/pdnsd/src/rr_types.c create mode 100644 app/src/main/jni/pdnsd/src/rr_types.h create mode 100644 app/src/main/jni/pdnsd/src/rr_types.in create mode 100644 app/src/main/jni/pdnsd/src/servers.c create mode 100644 app/src/main/jni/pdnsd/src/servers.h create mode 100644 app/src/main/jni/pdnsd/src/sort_namevalues.pl create mode 100644 app/src/main/jni/pdnsd/src/status.c create mode 100644 app/src/main/jni/pdnsd/src/status.h create mode 100644 app/src/main/jni/pdnsd/src/test/Makefile.am create mode 100644 app/src/main/jni/pdnsd/src/test/Makefile.in create mode 100644 app/src/main/jni/pdnsd/src/test/if_up.c create mode 100644 app/src/main/jni/pdnsd/src/test/is_local_addr.c create mode 100644 app/src/main/jni/pdnsd/src/test/random.c create mode 100644 app/src/main/jni/pdnsd/src/test/tping.c create mode 100644 app/src/main/jni/pdnsd/src/thread.c create mode 100644 app/src/main/jni/pdnsd/src/thread.h create mode 100644 app/src/main/jni/pdnsd/version create mode 100644 app/src/main/jni/system.cpp create mode 100755 app/src/main/jniLibs/arm64-v8a/libpdnsd.so create mode 100755 app/src/main/jniLibs/arm64-v8a/libsystem.so create mode 100755 app/src/main/jniLibs/arm64-v8a/libtun2socks.so create mode 100755 app/src/main/jniLibs/armeabi-v7a/libpdnsd.so create mode 100755 app/src/main/jniLibs/armeabi-v7a/libsystem.so create mode 100755 app/src/main/jniLibs/armeabi-v7a/libtun2socks.so create mode 100755 app/src/main/jniLibs/x86/libpdnsd.so create mode 100755 app/src/main/jniLibs/x86/libsystem.so create mode 100755 app/src/main/jniLibs/x86/libtun2socks.so create mode 100755 app/src/main/jniLibs/x86_64/libpdnsd.so create mode 100755 app/src/main/jniLibs/x86_64/libsystem.so create mode 100755 app/src/main/jniLibs/x86_64/libtun2socks.so create mode 100644 app/src/main/res/drawable-anydpi-v24/ic_vpn.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_vpn.png create mode 100644 app/src/main/res/drawable-mdpi/ic_vpn.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_vpn.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_vpn.png create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/layout/action_switch.xml create mode 100644 app/src/main/res/menu/main.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values-vi/strings.xml create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/values/ic_launcher_background.xml create mode 100644 app/src/main/res/values/pdnsd.xml create mode 100644 app/src/main/res/values/simpleroute.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/xml/backup_descriptor.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/main/res/xml/settings.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f70d177 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +*~ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..ae78c11 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,113 @@ + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..35c3a19 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..5b230c0 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..d2ce72d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f70bc04 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3d90694 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d4745e --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +SocksDroid +========== + +## SOCKS5 client for Android 5.0+ using VpnService + +Imported from : https://github.com/bndeff/socksdroid + +This is an updated version of [SocksDroid by PeterCxy](https://github.com/PeterCxy/SocksDroid) to support modern Android devices. + +The project is in maintenance mode: no new features are planned, only bug fixes and compatibility upgrades. + +[](https://play.google.com/store/apps/details?id=net.typeblog.socks) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..c0d4274 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 34 + defaultConfig { + applicationId "net.typeblog.socks" + minSdkVersion 21 + targetSdkVersion 33 + versionCode 13 + versionName "1.0.4" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + buildFeatures { + aidl true + } + packagingOptions { + jniLibs { + useLegacyPackaging true + } + } + namespace 'net.typeblog.socks' +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..ebce3e3 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/main/.gitignore b/app/src/main/.gitignore new file mode 100644 index 0000000..b672fde --- /dev/null +++ b/app/src/main/.gitignore @@ -0,0 +1 @@ +obj diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8c20f4f --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/aidl/net/typeblog/socks/IVpnService.aidl b/app/src/main/aidl/net/typeblog/socks/IVpnService.aidl new file mode 100644 index 0000000..788aa15 --- /dev/null +++ b/app/src/main/aidl/net/typeblog/socks/IVpnService.aidl @@ -0,0 +1,7 @@ +package net.typeblog.socks; + +interface IVpnService +{ + boolean isRunning(); + void stop(); +} diff --git a/app/src/main/build-jni.sh b/app/src/main/build-jni.sh new file mode 100755 index 0000000..d8bca70 --- /dev/null +++ b/app/src/main/build-jni.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -rf jniLibs + +ndk-build + +for p in armeabi-v7a arm64-v8a x86 x86_64; do + mv libs/$p/{tun2socks,libtun2socks.so} + mv libs/$p/{pdnsd,libpdnsd.so} +done + +mv libs jniLibs diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..763cdaa82b72362c90ce02c143b3d7627ea6749b GIT binary patch literal 6179 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 0) + p.setSummary(String.format(Locale.US, + String.format(Locale.US, "%%0%dd", p.getText().length()), 0) + .replace("0", "*")); + else + p.setSummary(""); + } + } + } + + private void resetTextN(EditTextPreference pref, Object newValue) { + if ((pref.getEditText().getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD) != InputType.TYPE_TEXT_VARIATION_PASSWORD) { + pref.setSummary(newValue.toString()); + } else { + String text = newValue.toString(); + if (text.length() > 0) + pref.setSummary(String.format(Locale.US, + String.format(Locale.US, "%%0%dd", text.length()), 0) + .replace("0", "*")); + else + pref.setSummary(""); + } + } + + private void addProfile() { + final EditText e = new EditText(getActivity()); + e.setSingleLine(true); + + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.prof_add) + .setView(e) + .setPositiveButton(android.R.string.ok, (d, which) -> { + String name = e.getText().toString().trim(); + + if (!TextUtils.isEmpty(name)) { + Profile p = mManager.addProfile(name); + + if (p != null) { + mProfile = p; + reload(); + return; + } + } + + Toast.makeText(getActivity(), + String.format(getString(R.string.err_add_prof), name), + Toast.LENGTH_SHORT).show(); + }) + .setNegativeButton(android.R.string.cancel, (d, which) -> { + + }) + .create().show(); + } + + private void removeProfile() { + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.prof_del) + .setMessage(String.format(getString(R.string.prof_del_confirm), mProfile.getName())) + .setPositiveButton(android.R.string.ok, (d, which) -> { + if (!mManager.removeProfile(mProfile.getName())) { + Toast.makeText(getActivity(), + getString(R.string.err_del_prof, mProfile.getName()), + Toast.LENGTH_SHORT).show(); + } else { + mProfile = mManager.getDefault(); + reload(); + } + }) + .setNegativeButton(android.R.string.cancel, (d, which) -> { + + }) + .create().show(); + } + + private void checkState() { + mRunning = false; + mSwitch.setEnabled(false); + mSwitch.setOnCheckedChangeListener(null); + + if (mBinder == null) { + getActivity().bindService(new Intent(getActivity(), SocksVpnService.class), mConnection, 0); + } + } + + private void updateState() { + if (mBinder == null) { + mRunning = false; + } else { + try { + mRunning = mBinder.isRunning(); + } catch (Exception e) { + mRunning = false; + } + } + + mSwitch.setChecked(mRunning); + + if ((!mStarting && !mStopping) || (mStarting && mRunning) || (mStopping && !mRunning)) { + mSwitch.setEnabled(true); + } + + if (mStarting && mRunning) { + mStarting = false; + } + + if (mStopping && !mRunning) { + mStopping = false; + } + + mSwitch.setOnCheckedChangeListener(ProfileFragment.this); + } + + private void startVpn() { + mStarting = true; + Intent i = VpnService.prepare(getActivity()); + + if (i != null) { + startActivityForResult(i, 0); + } else { + onActivityResult(0, Activity.RESULT_OK, null); + } + } + + private void stopVpn() { + if (mBinder == null) + return; + + mStopping = true; + + try { + mBinder.stop(); + } catch (Exception e) { + e.printStackTrace(); + } + + mBinder = null; + + getActivity().unbindService(mConnection); + checkState(); + } +} diff --git a/app/src/main/java/net/typeblog/socks/SocksVpnService.java b/app/src/main/java/net/typeblog/socks/SocksVpnService.java new file mode 100644 index 0000000..7d27acb --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/SocksVpnService.java @@ -0,0 +1,275 @@ +package net.typeblog.socks; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.net.VpnService; +import android.os.Build; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.text.TextUtils; +import android.util.Log; + +import net.typeblog.socks.util.Routes; +import net.typeblog.socks.util.Utility; + +import java.util.Locale; +import java.util.Objects; + +import static net.typeblog.socks.util.Constants.*; +import static net.typeblog.socks.BuildConfig.DEBUG; + +public class SocksVpnService extends VpnService { + class VpnBinder extends IVpnService.Stub { + @Override + public boolean isRunning() { + return mRunning; + } + + @Override + public void stop() { + stopMe(); + } + } + + private static final String TAG = SocksVpnService.class.getSimpleName(); + + private ParcelFileDescriptor mInterface; + private boolean mRunning = false; + private final IBinder mBinder = new VpnBinder(); + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + if (DEBUG) { + Log.d(TAG, "starting"); + } + + if (intent == null) { + return START_STICKY; + } + + if (mRunning) { + return START_STICKY; + } + + final String name = intent.getStringExtra(INTENT_NAME); + final String server = intent.getStringExtra(INTENT_SERVER); + final int port = intent.getIntExtra(INTENT_PORT, 1080); + final String username = intent.getStringExtra(INTENT_USERNAME); + final String passwd = intent.getStringExtra(INTENT_PASSWORD); + final String route = intent.getStringExtra(INTENT_ROUTE); + final String dns = intent.getStringExtra(INTENT_DNS); + final int dnsPort = intent.getIntExtra(INTENT_DNS_PORT, 53); + final boolean perApp = intent.getBooleanExtra(INTENT_PER_APP, false); + final boolean appBypass = intent.getBooleanExtra(INTENT_APP_BYPASS, false); + final String[] appList = intent.getStringArrayExtra(INTENT_APP_LIST); + final boolean ipv6 = intent.getBooleanExtra(INTENT_IPV6_PROXY, false); + final String udpgw = intent.getStringExtra(INTENT_UDP_GW); + + // Notifications on Oreo and above need a channel + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= 26) { + String NOTIFICATION_CHANNEL_ID = "net.typeblog.socks"; + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, + getString(R.string.channel_name), NotificationManager.IMPORTANCE_NONE); + NotificationManager notificationManager = getSystemService(NotificationManager.class); + Objects.requireNonNull(notificationManager).createNotificationChannel(channel); + builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID); + } else { + builder = new Notification.Builder(this); + } + + // Create the notification + int NOTIFICATION_ID = 1; + int intentFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + intentFlags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent contentIntent; + contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, MainActivity.class), intentFlags); + startForeground(NOTIFICATION_ID, builder + .setContentTitle(getString(R.string.notify_title)) + .setContentText(String.format(getString(R.string.notify_msg), name)) + .setPriority(Notification.PRIORITY_MIN) + .setSmallIcon(R.drawable.ic_vpn) + .setContentIntent(contentIntent) + .build()); + + // Create an fd. + configure(name, route, perApp, appBypass, appList, ipv6); + + if (DEBUG) + Log.d(TAG, "fd: " + mInterface.getFd()); + + if (mInterface != null) + start(mInterface.getFd(), server, port, username, passwd, dns, dnsPort, ipv6, udpgw); + + return START_STICKY; + } + + @Override + public void onRevoke() { + super.onRevoke(); + stopMe(); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + @Override + public void onDestroy() { + super.onDestroy(); + + stopMe(); + } + + private void stopMe() { + stopForeground(true); + + Utility.killPidFile(getFilesDir() + "/tun2socks.pid"); + Utility.killPidFile(getFilesDir() + "/pdnsd.pid"); + + try { + System.jniclose(mInterface.getFd()); + mInterface.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + stopSelf(); + } + + private void configure(String name, String route, boolean perApp, boolean bypass, String[] apps, boolean ipv6) { + Builder b = new Builder(); + b.setMtu(1500) + .setSession(name) + .addAddress("26.26.26.1", 24) + .addDnsServer("8.8.8.8"); + + if (ipv6) { + // Route all IPv6 traffic + b.addAddress("fdfe:dcba:9876::1", 126) + .addRoute("::", 0); + } + + Routes.addRoutes(this, b, route); + + // Add the default DNS + // Note that this DNS is just a stub. + // Actual DNS requests will be redirected through pdnsd. + b.addRoute("8.8.8.8", 32); + + // Do app routing + if (!perApp) { + // Just bypass myself + try { + b.addDisallowedApplication("net.typeblog.socks"); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + if (bypass) { + // First, bypass myself + try { + b.addDisallowedApplication("net.typeblog.socks"); + } catch (Exception e) { + e.printStackTrace(); + } + + for (String p : apps) { + if (TextUtils.isEmpty(p)) + continue; + + try { + b.addDisallowedApplication(p.trim()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } else { + for (String p : apps) { + if (TextUtils.isEmpty(p) || p.trim().equals("net.typeblog.socks")) { + continue; + } + + try { + b.addAllowedApplication(p.trim()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + mInterface = b.establish(); + } + + private void start(int fd, String server, int port, String user, String passwd, String dns, int dnsPort, boolean ipv6, String udpgw) { + // Start DNS daemon first + Utility.makePdnsdConf(this, dns, dnsPort); + + Utility.exec(String.format(Locale.US, "%s/libpdnsd.so -c %s/pdnsd.conf", + getApplicationInfo().nativeLibraryDir, getFilesDir())); + + String command = String.format(Locale.US, + "%s/libtun2socks.so --netif-ipaddr 26.26.26.2" + + " --netif-netmask 255.255.255.0" + + " --socks-server-addr %s:%d" + + " --tunfd %d" + + " --tunmtu 1500" + + " --loglevel 3" + + " --pid %s/tun2socks.pid" + + " --sock %s/sock_path" + , getApplicationInfo().nativeLibraryDir, server, port, fd, getFilesDir(), getApplicationInfo().dataDir); + + if (user != null) { + command += " --username " + user; + command += " --password " + passwd; + } + + if (ipv6) { + command += " --netif-ip6addr fdfe:dcba:9876::2"; + } + + command += " --dnsgw 26.26.26.1:8091"; + + if (udpgw != null) { + command += " --udpgw-remote-server-addr " + udpgw; + } + + if (DEBUG) { + Log.d(TAG, command); + } + + if (Utility.exec(command) != 0) { + stopMe(); + return; + } + + // Try to send the Fd through socket. + int i = 0; + while (i < 5) { + if (System.sendfd(fd, getApplicationInfo().dataDir + "/sock_path") != -1) { + mRunning = true; + return; + } + + i++; + + try { + Thread.sleep(1000L * i); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Should not get here. Must be a failure. + stopMe(); + } +} diff --git a/app/src/main/java/net/typeblog/socks/System.java b/app/src/main/java/net/typeblog/socks/System.java new file mode 100644 index 0000000..8d61e46 --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/System.java @@ -0,0 +1,11 @@ +package net.typeblog.socks; + +class System +{ + static { + java.lang.System.loadLibrary("system"); + } + + public static native int sendfd(int fd, String sock); + public static native void jniclose(int fd); +} diff --git a/app/src/main/java/net/typeblog/socks/util/Constants.java b/app/src/main/java/net/typeblog/socks/util/Constants.java new file mode 100644 index 0000000..8bf3f4e --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/util/Constants.java @@ -0,0 +1,41 @@ +package net.typeblog.socks.util; + +public class Constants +{ + public static final String ROUTE_ALL = "all", + ROUTE_CHN = "chn"; + + private static final String INTENT_PREFIX = "SOCKS"; + public static final String INTENT_NAME = INTENT_PREFIX + "NAME", + INTENT_SERVER = INTENT_PREFIX + "SERV", + INTENT_PORT = INTENT_PREFIX + "PORT", + INTENT_USERNAME = INTENT_PREFIX + "UNAME", + INTENT_PASSWORD = INTENT_PREFIX + "PASSWD", + INTENT_ROUTE = INTENT_PREFIX + "ROUTE", + INTENT_DNS = INTENT_PREFIX + "DNS", + INTENT_DNS_PORT = INTENT_PREFIX + "DNSPORT", + INTENT_PER_APP = INTENT_PREFIX + "PERAPP", + INTENT_APP_BYPASS = INTENT_PREFIX + "APPBYPASS", + INTENT_APP_LIST = INTENT_PREFIX + "APPLIST", + INTENT_IPV6_PROXY = INTENT_PREFIX + "IPV6", + INTENT_UDP_GW = INTENT_PREFIX + "UDPGW"; + + public static final String PREF = "profile", + PREF_PROFILE = "profile", + PREF_LAST_PROFILE = "last_profile", + PREF_SERVER_IP = "server_ip", + PREF_SERVER_PORT = "server_port", + PREF_IPV6_PROXY = "ipv6_proxy", + PREF_UDP_PROXY = "udp_proxy", + PREF_UDP_GW = "udp_gw", + PREF_AUTH_USERPW = "auth_userpw", + PREF_AUTH_USERNAME = "auth_username", + PREF_AUTH_PASSWORD = "auth_password", + PREF_ADV_ROUTE = "adv_route", + PREF_ADV_DNS = "adv_dns", + PREF_ADV_DNS_PORT = "adv_dns_port", + PREF_ADV_PER_APP = "adv_per_app", + PREF_ADV_APP_BYPASS = "adv_app_bypass", + PREF_ADV_APP_LIST = "adv_app_list", + PREF_ADV_AUTO_CONNECT = "adv_auto_connect"; +} diff --git a/app/src/main/java/net/typeblog/socks/util/Profile.java b/app/src/main/java/net/typeblog/socks/util/Profile.java new file mode 100644 index 0000000..2f83e45 --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/util/Profile.java @@ -0,0 +1,169 @@ +package net.typeblog.socks.util; + +import android.content.SharedPreferences; + +import static net.typeblog.socks.util.Constants.*; + +public class Profile { + private final SharedPreferences mPref; + private final String mName; + private final String mPrefix; + + Profile(SharedPreferences pref, String name) { + mPref = pref; + mName = name; + mPrefix = prefPrefix(name); + } + + public String getName() { + return mName; + } + + public String getServer() { + return mPref.getString(key("server"), "127.0.0.1"); + } + + public void setServer(String server) { + mPref.edit().putString(key("server"), server).apply(); + } + + public int getPort() { + return mPref.getInt(key("port"), 1080); + } + + public void setPort(int port) { + mPref.edit().putInt(key("port"), port).apply(); + } + + public boolean isUserPw() { + return mPref.getBoolean(key("userpw"), false); + } + + public void setIsUserpw(boolean is) { + mPref.edit().putBoolean(key("userpw"), is).apply(); + } + + public String getUsername() { + return mPref.getString(key("username"), ""); + } + + public void setUsername(String username) { + mPref.edit().putString(key("username"), username).apply(); + } + + public String getPassword() { + return mPref.getString(key("password"), ""); + } + + public void setPassword(String password) { + mPref.edit().putString(key("password"), password).apply(); + } + + public String getRoute() { + return mPref.getString(key("route"), ROUTE_ALL); + } + + public void setRoute(String route) { + mPref.edit().putString(key("route"), route).apply(); + } + + public String getDns() { + return mPref.getString(key("dns"), "8.8.8.8"); + } + + public void setDns(String dns) { + mPref.edit().putString(key("dns"), dns).apply(); + } + + public int getDnsPort() { + return mPref.getInt(key("dns_port"), 53); + } + + public void setDnsPort(int port) { + mPref.edit().putInt(key("dns_port"), port).apply(); + } + + public boolean isPerApp() { + return mPref.getBoolean(key("perapp"), false); + } + + public void setIsPerApp(boolean is) { + mPref.edit().putBoolean(key("perapp"), is).apply(); + } + + public boolean isBypassApp() { + return mPref.getBoolean(key("appbypass"), false); + } + + public void setIsBypassApp(boolean is) { + mPref.edit().putBoolean(key("appbypass"), is).apply(); + } + + public String getAppList() { + return mPref.getString(key("applist"), ""); + } + + public void setAppList(String list) { + mPref.edit().putString(key("applist"), list).apply(); + } + + public boolean hasIPv6() { + return mPref.getBoolean(key("ipv6"), false); + } + + public void setHasIPv6(boolean has) { + mPref.edit().putBoolean(key("ipv6"), has).apply(); + } + + public boolean hasUDP() { + return mPref.getBoolean(key("udp"), false); + } + + public void setHasUDP(boolean has) { + mPref.edit().putBoolean(key("udp"), has).apply(); + } + + public String getUDPGW() { + return mPref.getString(key("udpgw"), "127.0.0.1:7300"); + } + + public void setUDPGW(String gw) { + mPref.edit().putString(key("udpgw"), gw).apply(); + } + + public boolean autoConnect() { + return mPref.getBoolean(key("auto"), false); + } + + public void setAutoConnect(boolean auto) { + mPref.edit().putBoolean(key("auto"), auto).apply(); + } + + void delete() { + mPref.edit() + .remove(key("server")) + .remove(key("port")) + .remove(key("userpw")) + .remove(key("username")) + .remove(key("password")) + .remove(key("route")) + .remove(key("dns")) + .remove(key("dns_port")) + .remove(key("perapp")) + .remove(key("appbypass")) + .remove(key("applist")) + .remove(key("ipv6")) + .remove(key("udp")) + .remove(key("udpgw")) + .remove(key("auto")) + .apply(); + } + + private String key(String k) { + return mPrefix + k; + } + + private static String prefPrefix(String name) { + return name.replace("_", "__").replace(" ", "_"); + } +} diff --git a/app/src/main/java/net/typeblog/socks/util/ProfileManager.java b/app/src/main/java/net/typeblog/socks/util/ProfileManager.java new file mode 100644 index 0000000..d2b0715 --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/util/ProfileManager.java @@ -0,0 +1,87 @@ +package net.typeblog.socks.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +import net.typeblog.socks.R; +import static net.typeblog.socks.util.Constants.*; + +public class ProfileManager { + + private final SharedPreferences mPref; + private final Context mContext; + + public ProfileManager(Context context) { + mContext = context; + mPref = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE); + } + + private List getProfileList() { + List mProfiles = new ArrayList<>(); + mProfiles.add(mContext.getString(R.string.prof_default)); + + String[] profiles = mPref.getString(PREF_PROFILE, "").split("\n"); + + for (String p : profiles) { + if (!TextUtils.isEmpty(p)) { + mProfiles.add(p); + } + } + return mProfiles; + } + + public String[] getProfiles() { + return getProfileList().toArray(new String[0]); + } + + public Profile getProfile(String name) { + if (!getProfileList().contains(name)) { + return null; + } else { + return new Profile(mPref, name); + } + } + + public Profile getDefault() { + return new Profile(mPref, mPref.getString(PREF_LAST_PROFILE, getProfileList().get(0))); + } + + public void switchDefault(String name) { + if (getProfileList().contains(name)) + mPref.edit().putString(PREF_LAST_PROFILE, name).apply(); + } + + public Profile addProfile(String name) { + List mProfiles = getProfileList(); + if (mProfiles.contains(name)) { + return null; + } else { + mProfiles.add(name); + mProfiles.remove(0); + mPref.edit().putString(PREF_PROFILE, Utility.join(mProfiles, "\n")) + .putString(PREF_LAST_PROFILE, name).apply(); + return getDefault(); + } + } + + public boolean removeProfile(String name) { + List mProfiles = getProfileList(); + if (name.equals(mProfiles.get(0)) || !mProfiles.contains(name)) { + return false; + } + + new Profile(mPref, name).delete(); + + mProfiles.remove(0); + mProfiles.remove(name); + + mPref.edit().putString(PREF_PROFILE, Utility.join(mProfiles, "\n")) + .remove(PREF_LAST_PROFILE).apply(); + + return true; + } +} diff --git a/app/src/main/java/net/typeblog/socks/util/Routes.java b/app/src/main/java/net/typeblog/socks/util/Routes.java new file mode 100644 index 0000000..18a5fe2 --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/util/Routes.java @@ -0,0 +1,27 @@ +package net.typeblog.socks.util; + +import android.content.Context; +import android.net.VpnService; + +import net.typeblog.socks.R; +import static net.typeblog.socks.util.Constants.*; + +public class Routes { + public static void addRoutes(Context context, VpnService.Builder builder, String name) { + String[] routes; + if(ROUTE_CHN.equals(name)) { + routes = context.getResources().getStringArray(R.array.simple_route); + } else { + routes = new String[]{"0.0.0.0/0"}; + } + + for (String r : routes) { + String[] cidr = r.split("/"); + + // Cannot handle 127.0.0.0/8 + if (cidr.length == 2 && !cidr[0].startsWith("127")) { + builder.addRoute(cidr[0], Integer.parseInt(cidr[1])); + } + } + } +} diff --git a/app/src/main/java/net/typeblog/socks/util/Utility.java b/app/src/main/java/net/typeblog/socks/util/Utility.java new file mode 100644 index 0000000..5ca20e1 --- /dev/null +++ b/app/src/main/java/net/typeblog/socks/util/Utility.java @@ -0,0 +1,141 @@ +package net.typeblog.socks.util; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.util.List; + +import net.typeblog.socks.R; +import net.typeblog.socks.SocksVpnService; +import static net.typeblog.socks.util.Constants.*; + +public class Utility { + private static final String TAG = Utility.class.getSimpleName(); + + public static int exec(String cmd) { + try { + Process p = Runtime.getRuntime().exec(cmd); + + return p.waitFor(); + } catch (Exception e) { + return -1; + } + } + + public static void killPidFile(String f) { + File file = new File(f); + + if (!file.exists()) { + return; + } + + InputStream i; + try { + i = new FileInputStream(file); + } catch (Exception e) { + return; + } + + byte[] buf = new byte[512]; + int len; + StringBuilder str = new StringBuilder(); + + try { + while ((len = i.read(buf, 0, 512)) > 0) { + str.append(new String(buf, 0, len)); + } + i.close(); + } catch (Exception e) { + return; + } + + try { + int pid = Integer.parseInt(str.toString().trim().replace("\n", "")); + Runtime.getRuntime().exec("kill " + pid).waitFor(); + if(!file.delete()) + Log.w(TAG, "failed to delete pidfile"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String join(List list, String separator) { + if (list.isEmpty()) return ""; + + StringBuilder ret = new StringBuilder(); + + for (String s : list) { + ret.append(s).append(separator); + } + + return ret.substring(0, ret.length() - separator.length()); + } + + public static void makePdnsdConf(Context context, String dns, int port) { + String conf = context.getString(R.string.pdnsd_conf) + .replace("{DIR}", context.getFilesDir().toString()) + .replace("{IP}", dns) + .replace("{PORT}", Integer.toString(port)); + + File f = new File(context.getFilesDir() + "/pdnsd.conf"); + + if (f.exists()) { + if(!f.delete()) + Log.w(TAG, "failed to delete pdnsd.conf"); + } + + try { + OutputStream out = new FileOutputStream(f); + out.write(conf.getBytes()); + out.flush(); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + File cache = new File(context.getFilesDir() + "/pdnsd.cache"); + + if (!cache.exists()) { + try { + if(!cache.createNewFile()) + Log.w(TAG, "failed to create pdnsd.cache"); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static void startVpn(Context context, Profile profile) { + Intent i = new Intent(context, SocksVpnService.class) + .putExtra(INTENT_NAME, profile.getName()) + .putExtra(INTENT_SERVER, profile.getServer()) + .putExtra(INTENT_PORT, profile.getPort()) + .putExtra(INTENT_ROUTE, profile.getRoute()) + .putExtra(INTENT_DNS, profile.getDns()) + .putExtra(INTENT_DNS_PORT, profile.getDnsPort()) + .putExtra(INTENT_PER_APP, profile.isPerApp()) + .putExtra(INTENT_IPV6_PROXY, profile.hasIPv6()); + + if (profile.isUserPw()) { + i.putExtra(INTENT_USERNAME, profile.getUsername()) + .putExtra(INTENT_PASSWORD, profile.getPassword()); + } + + if (profile.isPerApp()) { + i.putExtra(INTENT_APP_BYPASS, profile.isBypassApp()) + .putExtra(INTENT_APP_LIST, profile.getAppList().split("\n")); + } + + if (profile.hasUDP()) { + i.putExtra(INTENT_UDP_GW, profile.getUDPGW()); + } + + context.startService(i); + } +} diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..4ac7f94 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,159 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +LOCAL_PATH := $(call my-dir) +ROOT_PATH := $(LOCAL_PATH) + +######################################################## +## pdnsd library +######################################################## + +include $(CLEAR_VARS) + +PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c) + +LOCAL_MODULE := pdnsd +LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%) +LOCAL_CFLAGS := -Wall -O2 -I$(LOCAL_PATH)/pdnsd -DHAVE_STPCPY + +include $(BUILD_EXECUTABLE) + +######################################################## +## libancillary +######################################################## + +include $(CLEAR_VARS) + +ANCILLARY_SOURCE := fd_recv.c fd_send.c + +LOCAL_MODULE := libancillary +LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libancillary + +LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## system +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE:= system + +LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary + +LOCAL_SRC_FILES:= system.cpp + +LOCAL_LDLIBS := -ldl -llog + +LOCAL_STATIC_LIBRARIES := cpufeatures libancillary + +include $(BUILD_SHARED_LIBRARY) + +######################################################## +## tun2socks +######################################################## + +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -std=gnu99 +LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE +LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL +LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE +LOCAL_CFLAGS += -DNDEBUG -DANDROID +# LOCAL_CFLAGS += -DTUN2SOCKS_JNI + +LOCAL_STATIC_LIBRARIES := libancillary + +LOCAL_C_INCLUDES:= \ + $(LOCAL_PATH)/libancillary \ + $(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \ + $(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \ + $(LOCAL_PATH)/badvpn/lwip/src/include \ + $(LOCAL_PATH)/badvpn/lwip/custom \ + $(LOCAL_PATH)/badvpn/ + +TUN2SOCKS_SOURCES := \ + base/BLog_syslog.c \ + system/BReactor_badvpn.c \ + system/BSignal.c \ + system/BConnection_unix.c \ + system/BTime.c \ + system/BUnixSignal.c \ + system/BNetwork.c \ + flow/StreamRecvInterface.c \ + flow/PacketRecvInterface.c \ + flow/PacketPassInterface.c \ + flow/StreamPassInterface.c \ + flow/SinglePacketBuffer.c \ + flow/BufferWriter.c \ + flow/PacketBuffer.c \ + flow/PacketStreamSender.c \ + flow/PacketPassConnector.c \ + flow/PacketProtoFlow.c \ + flow/PacketPassFairQueue.c \ + flow/PacketProtoEncoder.c \ + flow/PacketProtoDecoder.c \ + socksclient/BSocksClient.c \ + tuntap/BTap.c \ + lwip/src/core/timers.c \ + lwip/src/core/udp.c \ + lwip/src/core/memp.c \ + lwip/src/core/init.c \ + lwip/src/core/pbuf.c \ + lwip/src/core/tcp.c \ + lwip/src/core/tcp_out.c \ + lwip/src/core/netif.c \ + lwip/src/core/def.c \ + lwip/src/core/mem.c \ + lwip/src/core/tcp_in.c \ + lwip/src/core/stats.c \ + lwip/src/core/inet_chksum.c \ + lwip/src/core/ipv4/icmp.c \ + lwip/src/core/ipv4/igmp.c \ + lwip/src/core/ipv4/ip4_addr.c \ + lwip/src/core/ipv4/ip_frag.c \ + lwip/src/core/ipv4/ip4.c \ + lwip/src/core/ipv4/autoip.c \ + lwip/src/core/ipv6/ethip6.c \ + lwip/src/core/ipv6/inet6.c \ + lwip/src/core/ipv6/ip6_addr.c \ + lwip/src/core/ipv6/mld6.c \ + lwip/src/core/ipv6/dhcp6.c \ + lwip/src/core/ipv6/icmp6.c \ + lwip/src/core/ipv6/ip6.c \ + lwip/src/core/ipv6/ip6_frag.c \ + lwip/src/core/ipv6/nd6.c \ + lwip/custom/sys.c \ + tun2socks/tun2socks.c \ + base/DebugObject.c \ + base/BLog.c \ + base/BPending.c \ + system/BDatagram_unix.c \ + flowextra/PacketPassInactivityMonitor.c \ + tun2socks/SocksUdpGwClient.c \ + udpgw_client/UdpGwClient.c + +LOCAL_MODULE := tun2socks + +LOCAL_LDLIBS := -ldl -llog + +LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES)) + +include $(BUILD_EXECUTABLE) + +# Import cpufeatures +$(call import-module,android/cpufeatures) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..cc6dcde --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1,3 @@ +APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 +APP_PLATFORM := android-21 +APP_STL := c++_static diff --git a/app/src/main/jni/badvpn/CMakeLists.txt b/app/src/main/jni/badvpn/CMakeLists.txt new file mode 100644 index 0000000..d761e19 --- /dev/null +++ b/app/src/main/jni/badvpn/CMakeLists.txt @@ -0,0 +1,394 @@ +cmake_minimum_required(VERSION 2.8) +project(BADVPN C) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") + +include(TestBigEndian) +include(CheckIncludeFiles) +include(CheckSymbolExists) +include(CheckTypeSize) + +set(BUILD_COMPONENTS) + +macro (build_switch name text default) + if (BUILD_NOTHING_BY_DEFAULT) + option(BUILD_${name} "${text}" OFF) + else () + option(BUILD_${name} "${text}" "${default}") + endif () + list(APPEND BUILD_COMPONENTS "${name}") +endmacro () + +# detect Emscripten +if (CMAKE_C_COMPILER MATCHES "/emcc$") + set(EMSCRIPTEN ON) +else () + set(EMSCRIPTEN OFF) +endif () + +if (EMSCRIPTEN) + set(ON_IF_NOT_EMSCRIPTEN OFF) +else () + set(ON_IF_NOT_EMSCRIPTEN ON) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT EMSCRIPTEN) + set(ON_IF_LINUX ON) +else () + set(ON_IF_LINUX OFF) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR EMSCRIPTEN) + set(ON_IF_LINUX_OR_EMSCRIPTEN ON) +else () + set(ON_IF_LINUX_OR_EMSCRIPTEN OFF) +endif () + +# define build defaults +build_switch(EXAMPLES "build example programs" ON) +build_switch(TESTS "build some other example programs" ON) +build_switch(SERVER "build badvpn-server" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(CLIENT "build badvpn-client" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(FLOODER "build badvpn-flooder" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(TUN2SOCKS "build badvpn-tun2socks" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(UDPGW "build badvpn-udpgw" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(NCD "build badvpn-ncd" ${ON_IF_LINUX_OR_EMSCRIPTEN}) +build_switch(TUNCTL "build badvpn-tunctl" ${ON_IF_LINUX}) +build_switch(DOSTEST "build dostest-server and dostest-attacker" OFF) + +if (BUILD_NCD AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "NCD is only available on Linux") +endif () + +if (BUILD_CLIENT OR BUILD_SERVER) + find_package(OpenSSL REQUIRED) + set(LIBCRYPTO_INCLUDE_DIRS "${OpenSSL_INCLUDE_DIRS}") + set(LIBCRYPTO_LIBRARY_DIRS "${OpenSSL_LIBRARY_DIRS}") + set(LIBCRYPTO_LIBRARIES "${OpenSSL_LIBRARIES}") +endif () + +if (BUILD_SERVER OR BUILD_CLIENT OR BUILD_FLOODER) + find_package(NSPR REQUIRED) + find_package(NSS REQUIRED) +endif () + +# choose reactor +if (DEFINED BREACTOR_BACKEND) + if (NOT (BREACTOR_BACKEND STREQUAL "badvpn" OR BREACTOR_BACKEND STREQUAL "glib")) + message(FATAL_ERROR "unknown reactor backend specified") + endif () +else () + if (EMSCRIPTEN) + set(BREACTOR_BACKEND "emscripten") + else () + set(BREACTOR_BACKEND "badvpn") + endif () +endif () + +if (BREACTOR_BACKEND STREQUAL "badvpn") + add_definitions(-DBADVPN_BREACTOR_BADVPN) +elseif (BREACTOR_BACKEND STREQUAL "glib") + if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "GLib reactor backend is only available on Linux") + endif () + find_package(GLIB2 REQUIRED) + add_definitions(-DBADVPN_BREACTOR_GLIB) +elseif (BREACTOR_BACKEND STREQUAL "emscripten") + add_definitions(-DBADVPN_BREACTOR_EMSCRIPTEN) +endif () + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBCRYPTO_INCLUDE_DIRS} + ${NSPR_INCLUDE_DIRS} + ${NSS_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIR} + lwip/custom + lwip/src/include + lwip/src/include/ipv4 + lwip/src/include/ipv6 +) + +link_directories( + ${LIBCRYPTO_LIBRARY_DIRS} + ${NSPR_LIBRARY_DIRS} + ${NSS_LIBRARY_DIRS} +) + +test_big_endian(BIG_ENDIAN) + +check_type_size(int INT_SIZE) +if (NOT (INT_SIZE GREATER "3")) + message(FATAL_ERROR "int must be at least 32 bits") +endif () + +check_type_size(size_t SIZE_SIZE) +if (NOT (SIZE_SIZE GREATER INT_SIZE OR SIZE_SIZE EQUAL INT_SIZE)) + message(FATAL_ERROR "size_t must be greater or equal than int") +endif () + +if (MSVC) + add_definitions(/TP -D_CRT_SECURE_NO_WARNINGS /wd4065 /wd4018 /wd4533 /wd4244 /wd4102) +else () + add_definitions(-std=gnu99 -Wall -Wno-unused-value -Wno-parentheses -Wno-switch -Wredundant-decls) + + if (NOT CMAKE_C_COMPILER_ID STREQUAL "PathScale") + add_definitions(-Werror=implicit-function-declaration -Wno-switch-enum -Wno-unused-function + -Wstrict-aliasing) + endif () + + if (CMAKE_C_COMPILER_ID MATCHES "^Clang") + add_definitions(-Wno-initializer-overrides -Wno-tautological-constant-out-of-range-compare) + endif () +endif () + +# platform-specific stuff +if (WIN32) + add_definitions(-DBADVPN_USE_WINAPI -D_WIN32_WINNT=0x600 -DWIN32_LEAN_AND_MEAN) + add_definitions(-DBADVPN_THREAD_SAFE=0) + + set(CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=0x600") + check_symbol_exists(WSAID_WSASENDMSG "winsock2.h;mswsock.h" HAVE_MSW_1) + check_symbol_exists(WSAID_WSARECVMSG "winsock2.h;mswsock.h" HAVE_MSW_2) + check_symbol_exists(WSAID_ACCEPTEX "winsock2.h;mswsock.h" HAVE_MSW_3) + check_symbol_exists(WSAID_GETACCEPTEXSOCKADDRS "winsock2.h;mswsock.h" HAVE_MSW_4) + check_symbol_exists(WSAID_CONNECTEX "winsock2.h;mswsock.h" HAVE_MSW_5) + set(CMAKE_REQUIRED_DEFINITIONS "") + if (NOT (HAVE_MSW_1 AND HAVE_MSW_2 AND HAVE_MSW_3 AND HAVE_MSW_4 AND HAVE_MSW_5)) + add_definitions(-DBADVPN_USE_SHIPPED_MSWSOCK) + check_type_size(WSAMSG HAVE_WSAMSG) + if (NOT HAVE_WSAMSG) + add_definitions(-DBADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG) + endif () + endif () +else () + set(BADVPN_THREADWORK_USE_PTHREAD 1) + add_definitions(-DBADVPN_THREADWORK_USE_PTHREAD) + add_definitions(-DBADVPN_THREAD_SAFE=1) + + link_libraries(rt) + + if (EMSCRIPTEN) + add_definitions(-DBADVPN_EMSCRIPTEN) + add_definitions(-DBADVPN_NO_PROCESS -DBADVPN_NO_UDEV -DBADVPN_NO_RANDOM) + elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_definitions(-DBADVPN_LINUX) + + check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H) + if (HAVE_SYS_SIGNALFD_H) + add_definitions(-DBADVPN_USE_SIGNALFD) + else () + add_definitions(-DBADVPN_USE_SELFPIPE) + endif () + + check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H) + if (HAVE_SYS_EPOLL_H) + add_definitions(-DBADVPN_USE_EPOLL) + else () + add_definitions(-DBADVPN_USE_POLL) + endif () + + check_include_files(linux/rfkill.h HAVE_LINUX_RFKILL_H) + if (HAVE_LINUX_RFKILL_H) + add_definitions(-DBADVPN_USE_LINUX_RFKILL) + set(BADVPN_USE_LINUX_RFKILL 1) + endif () + + check_include_files(linux/input.h HAVE_LINUX_INPUT_H) + if (HAVE_LINUX_INPUT_H) + add_definitions(-DBADVPN_USE_LINUX_INPUT) + set(BADVPN_USE_LINUX_INPUT 1) + endif () + + check_include_files(sys/inotify.h HAVE_SYS_INOTIFY_H) + if (HAVE_SYS_INOTIFY_H) + add_definitions(-DBADVPN_USE_INOTIFY) + set(BADVPN_USE_INOTIFY 1) + endif () + elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_definitions(-DBADVPN_FREEBSD) + + check_symbol_exists(kqueue "sys/types.h;sys/event.h;sys/time.h" HAVE_KQUEUE) + if (NOT HAVE_KQUEUE) + message(FATAL_ERROR "kqueue is required") + endif () + add_definitions(-DBADVPN_USE_KEVENT) + endif () + + if (NOT DEFINED BADVPN_WITHOUT_CRYPTODEV) + check_include_files(crypto/cryptodev.h HAVE_CRYPTO_CRYPTODEV_H) + if (HAVE_CRYPTO_CRYPTODEV_H) + add_definitions(-DBADVPN_USE_CRYPTODEV) + elseif (DEFINED BADVPN_WITH_CRYPTODEV) + message(FATAL_ERROR "crypto/cryptodev.h not found") + endif () + endif () +endif () + +# check for syslog +check_include_files(syslog.h HAVE_SYSLOG_H) +if (HAVE_SYSLOG_H) + add_definitions(-DBADVPN_USE_SYSLOG) +endif () + +# add preprocessor definitions +if (BIG_ENDIAN) + add_definitions(-DBADVPN_BIG_ENDIAN) +else () + add_definitions(-DBADVPN_LITTLE_ENDIAN) +endif () + +# install man pages +install( + FILES badvpn.7 + DESTINATION share/man/man7 +) + +# reset variables indicating whether we're building various libraries, +# and set them in the respective CMakeLists files. This is used to disable +# building examples and tests which require libraries that are not available. +set(BUILDING_SECURITY 0) +set(BUILDING_DHCPCLIENT 0) +set(BUILDING_ARPPROBE 0) +set(BUILDING_BKIO 0) +set(BUILDING_PREDICATE 0) +set(BUILDING_UDEVMONITOR 0) +set(BUILDING_THREADWORK 0) +set(BUILDING_RANDOM 0) + +function(badvpn_add_library LIB_NAME LINK_BADVPN_LIBS LINK_SYS_LIBS LIB_SOURCES) + set(BADVPN_LIBS_EXEC) + set(BADVPN_LIBS_PLUGIN) + foreach(LIB ${LINK_BADVPN_LIBS}) + list(APPEND BADVPN_LIBS_EXEC "${LIB}") + list(APPEND BADVPN_LIBS_PLUGIN "${LIB}-plugin") + endforeach() + + add_library("${LIB_NAME}" STATIC ${LIB_SOURCES}) + target_link_libraries("${LIB_NAME}" ${BADVPN_LIBS_EXEC} ${LINK_SYS_LIBS}) + + add_library("${LIB_NAME}-plugin" STATIC ${LIB_SOURCES}) + target_link_libraries("${LIB_NAME}-plugin" ${BADVPN_LIBS_PLUGIN} ${LINK_SYS_LIBS}) + set_target_properties("${LIB_NAME}-plugin" PROPERTIES POSITION_INDEPENDENT_CODE YES) + set_target_properties("${LIB_NAME}-plugin" PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -DBADVPN_PLUGIN") +endfunction() + +# internal libraries +add_subdirectory(base) +add_subdirectory(system) +add_subdirectory(flow) +add_subdirectory(flowextra) +if (OpenSSL_FOUND) + set(BUILDING_SECURITY 1) + add_subdirectory(security) +endif () +if (NSS_FOUND) + add_subdirectory(nspr_support) +endif () +if (BUILD_CLIENT OR BUILDING_SECURITY) + set(BUILDING_THREADWORK 1) + add_subdirectory(threadwork) +endif () +if (BUILD_CLIENT OR BUILD_TUN2SOCKS) + add_subdirectory(tuntap) +endif () +if (BUILD_SERVER) + set(BUILDING_PREDICATE 1) + add_subdirectory(predicate) +endif () +if (BUILD_CLIENT OR BUILD_FLOODER) + add_subdirectory(server_connection) +endif () +if (BUILD_NCD AND NOT EMSCRIPTEN) + set(BUILDING_DHCPCLIENT 1) + set(BUILDING_ARPPROBE 1) + set(BUILDING_UDEVMONITOR 1) + set(BUILDING_RANDOM 1) + add_subdirectory(stringmap) + add_subdirectory(udevmonitor) + add_subdirectory(dhcpclient) + add_subdirectory(arpprobe) + add_subdirectory(random) +endif () +if (BUILD_TUN2SOCKS) + add_subdirectory(socksclient) + add_subdirectory(udpgw_client) + add_subdirectory(lwip) +endif () +if (BUILD_TUNCTL) + add_subdirectory(tunctl) +endif () + +# example programs +if (BUILD_EXAMPLES) + add_subdirectory(examples) +endif () + +# tests +if (BUILD_TESTS) + add_subdirectory(tests) +endif () + +# server +if (BUILD_SERVER) + add_subdirectory(server) +endif () + +# client +if (BUILD_CLIENT) + add_subdirectory(client) +endif () + +# flooder +if (BUILD_FLOODER) + add_subdirectory(flooder) +endif () + +# tun2socks +if (BUILD_TUN2SOCKS) + add_subdirectory(tun2socks) +endif () + +# udpgw +if (BUILD_UDPGW) + add_subdirectory(udpgw) +endif () + +# ncd +if (BUILD_NCD) + add_subdirectory(ncd) + if (NOT EMSCRIPTEN) + add_subdirectory(ncd-request) + endif () +endif () + +# dostest +if (BUILD_DOSTEST) + add_subdirectory(dostest) +endif () + +message(STATUS "Building components:") + +# print what we're building and what not +foreach (name ${BUILD_COMPONENTS}) + # to lower name + string(TOLOWER "${name}" name_withspaces) + + # append spaces to name + #while (TRUE) + # string(LENGTH "${name_withspaces}" length) + # if (NOT (length LESS 12)) + # break() + # endif () + # set(name_withspaces "${name_withspaces} ") + #endwhile () + + # determine if we're building + if (BUILD_${name}) + set(building "yes") + else () + set(building "no") + endif () + + message(STATUS " ${name_withspaces} ${building}") +endforeach () diff --git a/app/src/main/jni/badvpn/COPYING b/app/src/main/jni/badvpn/COPYING new file mode 100644 index 0000000..f973347 --- /dev/null +++ b/app/src/main/jni/badvpn/COPYING @@ -0,0 +1,24 @@ +Copyright (c) 2009, Ambroz Bizjak +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/src/main/jni/badvpn/ChangeLog b/app/src/main/jni/badvpn/ChangeLog new file mode 100644 index 0000000..ebfce63 --- /dev/null +++ b/app/src/main/jni/badvpn/ChangeLog @@ -0,0 +1,192 @@ +Version 1.999.127: + +- client, server: implement experimental support for performing SSL operations in worker threads. Currently it's rather inefficient. + +- NCD: modules: value: implement value::append() for appending to a list + +- NCD: modules: net_iptables: add single-argument form of append and insert commands, allowing for generic use + +- NCD: modules: net_iptables: implement net.iptables.insert() and net.ebtables.insert() + +- NCD: modules: sys_start_process: implement options, including username, term_on_deinit and deinit_kill_time + +- NCD: modules: sys_request_server: implement _caller in request handler + +- NCD: modules: add getenv() + +- NCD: modules: daemon: implement options, including username option + +- NCD: modules: runonce: add new options format with a map, implement username option + +- NCD: modules: add buffer(), which exposes a buffer with efficient appending and removing from the beginning. + +- NCD: add a new internal string representation called ComposedString. This allows modules to expose the concatenation of multiple memroy buffers as a single string value, efficiently. + +- fix many, hopefully all, strict aliasing violations. In particular, this fixes a bug where the DHCP client integrated into NCD won't work correctly, subject to optimization flags. + +- NCD: modules: sleep: interpret empty string as no sleeping, add sleep(ms_start) with one argument + +- NCD: modules: add log(), log_r() and log_fr() commands for logging via the BLog system + +Version 1.999.126: + +- NCD: modules: sleep: interpret empty string time as no sleeping, add sleep(ms_start) with one argument + +- NCD: modules: add log module for message logging using the BLog system + +- NCD: implement the "include" and "include_guard" directives, which allow separating reusable code into files + +- NCD: modules: call2: implement call_with_caller_target(), which makes it easier to write reusable code that calls back user-provided code + +- NCD: modules: call2: remove call2_if(), call2_ifelse(), embcall2(), embcall2_if(), embcall2_ifelse() + +- NCD: modules: add sys.start_process(), which implements starting and controlling external processes and reading/writing their stdout/stdin + +- tun2socks: implement SOCKS password authentication + +- NCD: track the depth of values and limit the maximum depth. This avoids stack overflow with very deeply nested values. + +- NCD: modules: add substr() + +- NCD: process_manager: add 2-argument start() method which doesn't take a process identifier + +- NCD: process_manager: allow process identifiers to be any value not just strings + +- NCD: multidepend, depend_scope: fix immediate effect order when a depend finishes backtracking + +- NCD: add depend_scope module to do exactly what the multidepend module does, but with separate non-global dependency name scopes + +- NCD: multidepend: allow dependency names to be any value not just strings + +- NCD: implement value::insert(what) for appending to a list + +- NCD: change the format of addresses in sys.request_server() and sys.request_client() to be the same as in the socket module + +- NCD: add socket module (sys.connect() and sys.listen()) + +- NCD: fix bug where duplicate template/process names would not be detected and weird behaviour would result + +- NCD: add backtrack_point() for simple backtracking + +- NCD: add file_open() for more complete file I/O + +- NCD: implement parse_ipv6_addr() and parse_ipv6_cidr_addr() + +- NCD: port to Emscripten/Javascript, for the in-browser demo + +- NCD: many performance and memory usage improvements + +- NCD: add assert_false() + +- NCD: don't link to OpenSSL to for random number generator. Use /dev/urandom instead to generate XIDs for DHCP. + +- NCD: deprecate ip_in_network() and instead add net.ipv{4,6}.addr_in_network(), net.ipv{4,6}.ifnot_addr_in_network() + +- NCD: implement some IPv6 modules: net.ipv6.addr(), net.ipv6.route() + +- NCD: support CIDR style addr/prefix addresses in various modules + +- NCD: recognize Elif and Else with capital first letter to be consistent with other reserved keywords + +Version 1.999.123: + +- NCD: performance improvements related to finding modules for statements + +- NCD: performance improvements related to resolving object names + +- NCD: performance improvements related to instantiating statement arguments + +- NCD: add value::replace_this() and value::replace_this_undo() + +- NCD: add value::reset() + +- NCD: add value::replace() and value::replace_undo() + +- Port to compile with MSVC for Windows. + +- NCD: add Foreach clause + +- NCD: implement _caller in spawn(), add spawn::join() + +- NCD: add explode() + +- NCD: add hard_reboot() and hard_poweroff() + +- NCD: add file_stat() and file_lstat() + +- NCD: fix regex_replace() semantics. It was very broken because it did a complete replacement pass for every regex on the list, so it would match parts that have already been replaced, producing unexpected results. + +- NCD: small performance improvement + +Version 1.999.121: + +- NCD: improve error handling semantics; see http://code.google.com/p/badvpn/source/detail?r=1376 + +- NCD: fix assertion failure in sys.evdev() if a device error occurs (e.g. device unplugged) while an event is being processed. Similar fix in some other modules, but these may not be reproducable. + +- NCD: some more performance improvements + +- NCD: some performance improvements (~30% faster interpretation of cpu-bound code) + +- NCD: implemented If..elif..else clause. + +- NCD: net.backend.wpa_supplicant: fix to work with wpa_supplicant>=1.0 + +Version 1.999.115: + +- NCD: Many improvements; new statements, including call(), alias(), foreach(), choose(). + +Version 1.999.113: + +- NCD: when starting child processes, make sure that file descriptors for standard + streams are always open in the child, by opening /dev/null if they are not. + +- Improve build system to allow selective building of components. + By default, everything is built, unless -DBUILD_NOTHING_BY_DEFAULT=1 is given. + Individual components can then be enabled or disabled using -DBUILD_COMPONENT=1 + and -DBUILD_COMPONENT=0. + +- When starting any BadVPN program, make sure that file descriptors for standard + streams are always open in the child, by opening /dev/null if they are not. + +- NCD: net.backend.wpa_supplicant(): add 'bssid' and 'ssid' variables to allow + determining what wireless network wpa_supplicant connected to. + +- NCD: net.backend.wpa_supplicant(): do not require the user to start wpa_supplicant via + stdbuf, but do it automatically. + +Version 1.999.111: + +- Improved protocol such that peers can use SSL when comminicating via the server. This + improves security, as compromising the server will not allow the attacker to see secret + data shared by peers (in particular, encryption keys and OTP seeds when in UDP mode). + + Compatibility is preserved if an only if the following conditions are met: + - The server is using the latest version. + - If the network is using SSL, all clients using the new version are using the + "--allow-peer-talk-without-ssl" command line option. + + Be aware, however, that using the "--allow-peer-talk-without-ssl" option negates the + security benefits of the new SSL support - not only between pairs of peers where one + peer is using the old version, but also between pairs where both peers are capable + of SSL. This is because the server can re-initialize the pair, telling them not to use + SSL. + +Version 1.999.107: + +- Added Windows IOCP support, removing the limitation on ~64 connections. This is important + for tun2socks, which may have to handle several hundred connections. + +Version 1.999.105.2: + +- Fixed an assertion failure in tun2socks related to sending data to SOCKS. + +Version 1.999.101.3: + +- Fixed UDP transport on Windows 7 which didn't work (was only tested on XP). + +Version 1.999.101: + +- Fixed a protocol issue present in versions <=1.999.100.3. Compatibility is preserved in + case of a new server and old clients, but it is not possible to connect to an old server + with a new client. diff --git a/app/src/main/jni/badvpn/INSTALL b/app/src/main/jni/badvpn/INSTALL new file mode 100644 index 0000000..3605f4e --- /dev/null +++ b/app/src/main/jni/badvpn/INSTALL @@ -0,0 +1,76 @@ +1 Requirements + +1.1 Operating system + +Linux: +- Linux kernel 2.6. Kernel 2.4 will work, but performance will suffer. +- tested on x86, x86_64 and ARM architectures. Not tested on any big-endian architecture. + +Windows: +- Windows XP or newer; tested on Windows XP and Windows 7 + +FreeBSD: +- Not regularly tested. + +Other systems are not supported. + +1.2 Compilers + +Linux: + - gcc + - clang, except >=3.0 (clang bug http://llvm.org/bugs/show_bug.cgi?id=11535) + +Windows: + - gcc from the mingw-w64 project for 32-bit targets + +C language features used: + - Standard (all part of C99): + - designated initializers + - stdint.h, inttypes.h, stddef.h + - intermingled declarations and code + - for loop initial declaration + - one-line "//" comments + - Extensions: + - packed structure attribute (to pack a structure and allow unaligned access) + +1.3 CMake + +The build system uses CMake. + +1.4 OpenSSL + +Libcrypto (part of OpenSSL) is used for block ciphers, hash functions and random data generation. + +1.5 Network Security Services (NSS) + +The NSS library from Mozilla is used for TLS support. NSS command-line tools are also needed +for setting up certificates. + +1.6 TAP-Win32 (Windows only) (runtime only) + +The TAP-Win32 driver, part of OpenVPN. + +2 Compilation + +2.1 Compiling on Linux + +$ tar xf badvpn-.tar.bz2 +$ mkdir build +$ cd build +$ cmake ../badvpn- -DCMAKE_INSTALL_PREFIX=/usr/local +$ make +If you want to install it, run as root: +# make install + +If you only want NCD or tun2socks and not the VPN system, you can avoid the NSS dependency by passing +the following to the cmake command: +-DBUILD_NCD=1 -DBUILD_TUN2SOCKS=1 -DBUILD_NOTHING_BY_DEFAULT=1 + +2.2 Compiling for Windows + +See the file INSTALL-WINDOWS for detailed instructions. + +3 Usage + +The primary documentation is on the BadVPN homepage, http://code.google.com/p/badvpn/ . +Additionally, some man pages are installed (badvpn(7), badvpn-server(8), badvpn-client(8)). diff --git a/app/src/main/jni/badvpn/INSTALL-WINDOWS b/app/src/main/jni/badvpn/INSTALL-WINDOWS new file mode 100644 index 0000000..9f0d5cf --- /dev/null +++ b/app/src/main/jni/badvpn/INSTALL-WINDOWS @@ -0,0 +1,72 @@ +There are many ways to build BadVPN for Windows. It can be built with MSVC or GCC compilers, +and it be built natively from Windows or cross-compiled from Linux. However, this document +only describes building natively from Windows using MSVC. + +1. Get a MSVC compiler, e.g. from Visual Studio, Visual Studio Express or from the Windows SDK. + +2. Choose a directory where built stuff will be installed into; we call it . + +3. Build the NSS library. + NOTE: you can also use the prebuilt version in the BadVPN windows download. + + - Install MozillaBuild: + http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe . + + - Download the NSS source code that includes NSPR. As of the time of writing the latest version was 3.13.5: + https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_13_5_RTM/src/nss-3.13.5-with-nspr-4.9.1.tar.gz . + + Extract it to c:\ so that you have C:\mozilla . + + - Open a terminal with access to the Visual Studio compilers and other tools. E.g. if you use the Windows SDK, + activate the following start menu item: Programs -> Microsoft Windows SDK v7.1 -> Windows SDK 7.1 Command Prompt. + + - In this terminal, run: + + > c:\mozilla-build\start-l10n.bat + + - Either a new terminal opens with a bash shell, or a bash shell starts in the existing terminal. Either way, + enter the following commands to finally build NSS: (here paths are written as /driveletter/...) + + $ export OS_TARGET=WINNT + $ export BUILD_OPT=1 + $ cd /mozilla/security/nss + $ make nss_build_all + + Now use a script shipped with the BadVPN source to copy the resulting files into appropriate directories within : + + $ /scripts/copy_nss ../../dist + +4. Build the OpenSSL library. + NOTE: you can also use the prebuilt version in the BadVPN windows download. + + - Install ActivePerl. + + - Download the OpenSSL source code and extract it. + + - Open a compiler terminal, as was done when building NSS. Inside it, run: + + > cd + > perl Configure VC-WIN32 --prefix= + > ms\do_ms + > nmake -f ms\ntdll.mak + + To copy the results into : + + > nmake -f ms\ntdll.mak install + +5. Build BadVPN. + + - Install CMake. During installation, select the option to include cmake in PATH + to avoid having to type a long path into the terminal. + + - Create an empty folder where BadVPN will be built; call it . + + - Open a compiler terminal. Inside it, run: + + > cd + > cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release + > nmake + + To copy the results into : + + > nmake install diff --git a/app/src/main/jni/badvpn/arpprobe/BArpProbe.c b/app/src/main/jni/badvpn/arpprobe/BArpProbe.c new file mode 100644 index 0000000..2e6feb4 --- /dev/null +++ b/app/src/main/jni/badvpn/arpprobe/BArpProbe.c @@ -0,0 +1,359 @@ +/** + * @file BArpProbe.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "BArpProbe.h" + +#include + +#define STATE_INITIAL 1 +#define STATE_NOEXIST 2 +#define STATE_EXIST 3 +#define STATE_EXIST_PANIC 4 + +static void dgram_handler (BArpProbe *o, int event) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "packet socket error"); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BARPPROBE_EVENT_ERROR)); + return; +} + +static void send_request (BArpProbe *o) +{ + if (o->send_sending) { + BLog(BLOG_ERROR, "cannot send packet while another packet is being sent!"); + return; + } + + // build packet + struct arp_packet *arp = &o->send_packet; + arp->hardware_type = hton16(ARP_HARDWARE_TYPE_ETHERNET); + arp->protocol_type = hton16(ETHERTYPE_IPV4); + arp->hardware_size = hton8(6); + arp->protocol_size = hton8(4); + arp->opcode = hton16(ARP_OPCODE_REQUEST); + memcpy(arp->sender_mac, o->if_mac, 6); + arp->sender_ip = hton32(0); + memset(arp->target_mac, 0, sizeof(arp->target_mac)); + arp->target_ip = o->addr; + + // send packet + PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)&o->send_packet, sizeof(o->send_packet)); + + // set sending + o->send_sending = 1; +} + +static void send_if_handler_done (BArpProbe *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send_sending) + + // set not sending + o->send_sending = 0; +} + +static void recv_if_handler_done (BArpProbe *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= sizeof(struct arp_packet)) + + // receive next packet + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet); + + if (data_len != sizeof(struct arp_packet)) { + BLog(BLOG_WARNING, "receive: wrong size"); + return; + } + + struct arp_packet *arp = &o->recv_packet; + + if (ntoh16(arp->hardware_type) != ARP_HARDWARE_TYPE_ETHERNET) { + BLog(BLOG_WARNING, "receive: wrong hardware type"); + return; + } + + if (ntoh16(arp->protocol_type) != ETHERTYPE_IPV4) { + BLog(BLOG_WARNING, "receive: wrong protocol type"); + return; + } + + if (ntoh8(arp->hardware_size) != 6) { + BLog(BLOG_WARNING, "receive: wrong hardware size"); + return; + } + + if (ntoh8(arp->protocol_size) != 4) { + BLog(BLOG_WARNING, "receive: wrong protocol size"); + return; + } + + if (ntoh16(arp->opcode) != ARP_OPCODE_REPLY) { + return; + } + + if (arp->sender_ip != o->addr) { + return; + } + + int old_state = o->state; + + // set minus one missed + o->num_missed = -1; + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITSEND); + + // set state exist + o->state = STATE_EXIST; + + // report exist if needed + if (old_state == STATE_INITIAL || old_state == STATE_NOEXIST) { + o->handler(o->user, BARPPROBE_EVENT_EXIST); + return; + } +} + +static void timer_handler (BArpProbe *o) +{ + DebugObject_Access(&o->d_obj); + + // send request + send_request(o); + + switch (o->state) { + case STATE_INITIAL: { + ASSERT(o->num_missed >= 0) + ASSERT(o->num_missed < BARPPROBE_INITIAL_NUM_ATTEMPTS) + + // increment missed + o->num_missed++; + + // all attempts failed? + if (o->num_missed == BARPPROBE_INITIAL_NUM_ATTEMPTS) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + + // set state noexist + o->state = STATE_NOEXIST; + + // report noexist + o->handler(o->user, BARPPROBE_EVENT_NOEXIST); + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV); + } break; + + case STATE_NOEXIST: { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + } break; + + case STATE_EXIST: { + ASSERT(o->num_missed >= -1) + ASSERT(o->num_missed < BARPPROBE_EXIST_NUM_NOREPLY) + + // increment missed + o->num_missed++; + + // all missed? + if (o->num_missed == BARPPROBE_EXIST_NUM_NOREPLY) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV); + + // set zero missed + o->num_missed = 0; + + // set state panic + o->state = STATE_EXIST_PANIC; + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITRECV); + } break; + + case STATE_EXIST_PANIC: { + ASSERT(o->num_missed >= 0) + ASSERT(o->num_missed < BARPPROBE_EXIST_PANIC_NUM_NOREPLY) + + // increment missed + o->num_missed++; + + // all missed? + if (o->num_missed == BARPPROBE_EXIST_PANIC_NUM_NOREPLY) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + + // set state panic + o->state = STATE_NOEXIST; + + // report noexist + o->handler(o->user, BARPPROBE_EVENT_NOEXIST); + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV); + } break; + } +} + +int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) +{ + ASSERT(ifname) + ASSERT(handler) + + // init arguments + o->addr = addr; + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // get interface information + int if_mtu; + int if_index; + if (!badvpn_get_iface_info(ifname, o->if_mac, &if_mtu, &if_index)) { + BLog(BLOG_ERROR, "failed to get interface information"); + goto fail0; + } + + uint8_t *if_mac = o->if_mac; + BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d", + if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index); + + // check MTU + if (if_mtu < sizeof(struct arp_packet)) { + BLog(BLOG_ERROR, "MTU is too small for ARP !?!"); + goto fail0; + } + + // init dgram + if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) { + BLog(BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // bind dgram + BAddr bind_addr; + BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac); + if (!BDatagram_Bind(&o->dgram, bind_addr)) { + BLog(BLOG_ERROR, "BDatagram_Bind failed"); + goto fail1; + } + + // set dgram send addresses + BAddr dest_addr; + uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac); + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr); + + // init send interface + BDatagram_SendAsync_Init(&o->dgram, sizeof(struct arp_packet)); + o->send_if = BDatagram_SendAsync_GetIf(&o->dgram); + PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_if_handler_done, o); + + // set not sending + o->send_sending = 0; + + // init recv interface + BDatagram_RecvAsync_Init(&o->dgram, sizeof(struct arp_packet)); + o->recv_if = BDatagram_RecvAsync_GetIf(&o->dgram); + PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_if_handler_done, o); + + // init timer + BTimer_Init(&o->timer, 0, (BTimer_handler)timer_handler, o); + + // receive first packet + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet); + + // send request + send_request(o); + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV); + + // set zero missed + o->num_missed = 0; + + // set state initial + o->state = STATE_INITIAL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void BArpProbe_Free (BArpProbe *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free timer + BReactor_RemoveTimer(o->reactor, &o->timer); + + // free recv interface + BDatagram_RecvAsync_Free(&o->dgram); + + // free send interface + BDatagram_SendAsync_Free(&o->dgram); + + // free dgram + BDatagram_Free(&o->dgram); +} diff --git a/app/src/main/jni/badvpn/arpprobe/BArpProbe.h b/app/src/main/jni/badvpn/arpprobe/BArpProbe.h new file mode 100644 index 0000000..2ec3ffa --- /dev/null +++ b/app/src/main/jni/badvpn/arpprobe/BArpProbe.h @@ -0,0 +1,80 @@ +/** + * @file BArpProbe.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BARPPROBE_H +#define BADVPN_BARPPROBE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BARPPROBE_INITIAL_WAITRECV 1000 +#define BARPPROBE_INITIAL_NUM_ATTEMPTS 6 +#define BARPPROBE_NOEXIST_WAITRECV 15000 +#define BARPPROBE_EXIST_WAITSEND 15000 +#define BARPPROBE_EXIST_WAITRECV 10000 +#define BARPPROBE_EXIST_NUM_NOREPLY 2 +#define BARPPROBE_EXIST_PANIC_WAITRECV 1000 +#define BARPPROBE_EXIST_PANIC_NUM_NOREPLY 6 + +#define BARPPROBE_EVENT_EXIST 1 +#define BARPPROBE_EVENT_NOEXIST 2 +#define BARPPROBE_EVENT_ERROR 3 + +typedef void (*BArpProbe_handler) (void *user, int event); + +typedef struct { + uint32_t addr; + BReactor *reactor; + void *user; + BArpProbe_handler handler; + BDatagram dgram; + uint8_t if_mac[6]; + PacketPassInterface *send_if; + int send_sending; + struct arp_packet send_packet; + PacketRecvInterface *recv_if; + struct arp_packet recv_packet; + BTimer timer; + int state; + int num_missed; + DebugError d_err; + DebugObject d_obj; +} BArpProbe; + +int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) WARN_UNUSED; +void BArpProbe_Free (BArpProbe *o); + +#endif diff --git a/app/src/main/jni/badvpn/arpprobe/CMakeLists.txt b/app/src/main/jni/badvpn/arpprobe/CMakeLists.txt new file mode 100644 index 0000000..cc03a81 --- /dev/null +++ b/app/src/main/jni/badvpn/arpprobe/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(arpprobe BArpProbe.c) +target_link_libraries(arpprobe base system flow) diff --git a/app/src/main/jni/badvpn/badvpn.7 b/app/src/main/jni/badvpn/badvpn.7 new file mode 100644 index 0000000..c421a35 --- /dev/null +++ b/app/src/main/jni/badvpn/badvpn.7 @@ -0,0 +1,324 @@ +.TH badvpn 7 "6 October 2010" +.SH NAME +BadVPN - peer-to-peer VPN system +.SH DESCRIPTION +.P +BadVPN is a peer-to-peer VPN system. It provides a Layer 2 (Ethernet) network between +the peers (VPN network nodes). The peers connect to a central server which acts as a chat +server for them to establish direct connections between each other (data connections). +These connections are used for transferring network data (Ethernet frames). +.SS "Features" +.P +.B "Data connections" +.P +Peers can transfer network data either over UDP or TCP. For both there are ways of +securing the data (see below). +.P +.B "IPv6 support" +.P +IPv6 can be used for both server connections and data connections, alongside with IPv4. +Additionally, both can be combined to allow gradual migration to IPv6. +.P +.B "Address selection" +.P +Because NATs and firewalls are widespread, it is harder for peer-to-peer services to operate. +In general, for two computers to be able to communicate, one computer must +.I bind +to one of its addresses, and the other computer must +.I connect +to the computer that binded (both for TCP and UDP). In a network with point-to-point +connectivity, the connecting computer can connect to the same address as the binding computer +bound to, so it is sufficient for the binding computer to send its address to the connecting +computer. However, NATs and firewalls break point-to-point connectivity. When a network is +behind a NAT, it is, by default, impossible for computers outside of that network to connect +to computers inside the network. This is because computers inside the network have no externally +visible IP address, and only communicate with the outside world through the external IP address +of the NAT router. It is however possible to manually configure the NAT router to +.I forward +a specific port number on its external IP address to a specific computer inside the network. +This makes it possible for a computer outside of the network to connect to a computer inside +a network, however, it must connect to the external address of the NAT router (rather than +the address the computer inside bound to, which is its internal address). So there needs +to be some way for the connecting peer to know what address to connect to. +.P +BadVPN solves this problem with so-called +.IR "address scopes" "." +The peer that binds must have a list of external addresses for each address it can bind to, +possibly ordered from best to worst. Each external address has its scope name. A scope name +represents part of a network from which an external address can be reached. On the other hand, +the peer that connects must have a list of scopes which it can reach. When a peer binds to an +address, it sends the other peer a list of external addresses along with scope names. That peer +than chooses the first external address whose scope it recognizes and attempts to connect to it +(if there is one). +.P +BadVPN also allows a peer to have multiple addresses for binding to. It is possible to specify +both an IPv4 and an IPv6 address to work in a multi-protocol environment. +.P +.B "Relaying" +.P +BadVPN can be configured to allow pairs of peers that cannot communicate directly (i.e. because of +NATs or firewalls) to relay network data through a third peer. Relaying is only attempted if +none of the two peers recognize any of the other peer's external addresses (or there are none). +For relaying to work, for each of the two peers (P1, other one P2) there must be at least one +third peer (R) that P1 it is allowed to relay through and can communicate directly with, and all +such peers R must be able to communicate directly with P2. +.P +.B "IGMP snooping" +.P +BadVPN nodes perform IGMP snooping in order to efficiently deliver multicast frames. For example, +this makes it possible to use BadVPN as a tunnel into an IPTV network of an Internet Service Provider +for you to watch TV from wherever you want (given sufficient link quality). +.P +.B "Code quality" +.P +BadVPN has great focus on code quality and reliability. BadVPN is written in the C programming +language. It is a single-threaded event-driven program. This allows for low resource usage and +fast response times. Even though C is a relatively low-level language, the programs are made of +small, highly cohesive and loosely coupled modules that are combined into a complete program on +a high level. Modules are accesed and communicate through small, simple and to-the-point interfaces. +It utilizes a flow-based design which greatly simplifies processing of data and input and output +of the programs. +.SS "Security features" +.P +BadVPN contains many security features, all of which are optional. The included security +features are described here. +.P +.B TLS for client-server connections +.P +It is possible for the peers to communicate with the chat server securely with TLS. It is +highly recommended that this feature is used if any security whatsoever is needed. Not +using it renders all other security features useless, since clients exchange keys +unencrypted via the server. When enabled, the chat server requires each client to identify +itself with a certificate. +.P +BadVPN uses Mozilla's NSS library for TLS support. This means that the required certificates +and keys must be available in a NSS database. The database and certificates can be +generated with the +.B certutil +command. See the examples section on how to generate and distribute the certificates. +.P +.B TLS for peer messaging +.P +If TLS is being used for client-server connections, it will also be used between each pair of +peers communicating via the server, on top of the TLS connections to the server. This secures +the messages from the server itself. It is important because the messages may include +encryption keys and other private data. +.P +.B TLS for TCP data connections +.P +If TCP is used for data connections between the peers, the data connections can be secured +with TLS. This requires using TLS for client-server connections. The clients need to trust +each others' certificates to be able to connect. Additionally, each client must identify to +its peers with the same certificates it used for connecting to the server. +.P +.B Encryption for UDP data connections +.P +If UDP is used for data connections, it is possible for each pair of peers to encrypt their +UDP packets with a symmetric block cipher. Note that the encryption keys are transmitted +through the server unencrypted, so for this to be useful, server connections must be secured +with TLS. The encryption aims to prevent third parties from seeing the real contents of +the network data being transfered. +.P +.B Hashes for UDP data connections +.P +If UDP is used for data connections, it is possible to include hashes in packets. Note that +hashes are only useful together with encryption. If enabled, the hash is calculated on the +packet with the hash field zeroed and then written to the hash field. Hashes are calculated +and included before encryption (if enabled). Combined with encryption, hashes aim to prevent +third parties from tampering with the packets and injecting them into the network. +.P +.B One-time passwords for UDP data connections +.P +If UDP is used for data connections, it is possible to include one-time passwords in packets. +Note that for this to be useful, server connections must be secured with TLS. +One-time passwords are generated from a seed value by encrypting zero data with a block cipher. +The seed contains the encryption key for the block cipher and the initialization vector. +Only a fixed number of passwords are used from a single seed. The peers exchange seeds through +the server. One-time passwords aim to prevent replay attacks. +.P +.B Control over peer communication +.P +It is possible to instruct the chat server to only allow certain peers to communicate. This +will break end-to-end connectivity in the virtual network. It is useful in certain cases +to improve security, for example when the VPN is used only to allow clients to securely connect +to a central service. +.SH "EXAMPLES" +.SS "Setting up certificates" +.P +If you want to use TLS for server connections (recommended), the server and all the peers will +need certificates. This section explains how to generate and distribute the certificates using +NSS command line tools. +.P +.B Setting up the Certificate Authority (CA) +.P +On the system that will host the CA, create a NSS database for the CA and generate a CA certificate +valid for 24 months: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -N +.br +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "vpnca" -s "CN=vpnca" -t "TC,," -x -2 -v 24 +.br +> Is this a CA certificate [y/N]? y +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > -1 +.br +> Is this a critical extension [y/N]? n +.P +Export the public CA certificate (this file is public): +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -L -n vpnca -a > ca.pem +.P +.B Setting up the server certificate +.P +On the CA system, generate a certificate for the server valid for 24 months, with TLS server usage context: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "" -s "CN=" -c "vpnca" -t ",," -2 -6 -v 24 +.br +> 0 +.br +> -1 +.br +> Is this a critical extension [y/N]? n +.br +> Is this a CA certificate [y/N]? n +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > +.br +> Is this a critical extension [y/N]? n +.P +Export the server certificate to a PKCS#12 file (this file must be kept secret): +.P +vpnca $ pk12util -d sql:/home/vpnca/nssdb -o server.p12 -n "" +.P +On the system that will run the server, create a NSS database and import the CA certificate +and the server cerificate: +.P +vpnserver $ certutil -d sql:/home/vpnserver/nssdb -N +.br +vpnserver $ certutil -d sql:/home/vpnserver/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem +.br +vpnserver $ pk12util -d sql:/home/vpnserver/nssdb -i /path/to/server.p12 +.P +.B Setting up peer certificates +.P +On the CA system, generate a certificate for the peer valid for 24 months, with TLS client and +TLS server usage contexts: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "peer-" -s "CN=peer-" -c "vpnca" -t ",," -2 -6 -v 24 +.br +> 0 +.br +> 1 +.br +> -1 +.br +> Is this a critical extension [y/N]? n +.br +> Is this a CA certificate [y/N]? n +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > +.br +> Is this a critical extension [y/N]? n +.P +Export the peer certificate to a PKCS#12 file (this file must be kept secret): +.P +vpnca $ pk12util -d sql:/home/vpnca/nssdb -o peer-.p12 -n "peer-" +.P +On the system that will run the VPN client, create a NSS database and import the CA certificate +and the peer cerificate: +.P +vpnclient $ certutil -d sql:/home/vpnclient/nssdb -N +.br +vpnclient $ certutil -d sql:/home/vpnclient/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem +.br +vpnclient $ pk12util -d sql:/home/vpnclient/nssdb -i /path/to/peer-.p12 +.SS "Setting up TAP devices" +.P +You need to create and configure TAP devices on all computers that will participate in the virtual network +(i.e. run the client program). See +.BR badvpn-client (8), +section `TAP DEVICE CONFIGURATION` for details. +.SS "Example: Local IPv4 network, UDP transport, zero security" +.P +.B Starting the server: +.P +badvpn-server --listen-addr 0.0.0.0:7000 +.P +.B Starting the peers: +.P +badvpn-client +.RS +--server-addr :7000 +.br +--transport-mode udp --encryption-mode none --hash-mode none +.br +--scope local1 +.br +--bind-addr 0.0.0.0:8000 --num-ports 30 --ext-addr {server_reported}:8000 local1 +.br +--tapdev tap0 +.RE +.SS "Example: Adding TLS and UDP security" +.P +.B Starting the server (other options as above): +.P +badvpn-server ... +.RS +--ssl --nssdb sql:/home/vpnserver/nssdb --server-cert-name "" +.RE +.P +.B Starting the peers (other options as above): +.P +badvpn-client ... +.RS +--ssl --nssdb sql:/home/vpnclient/nssdb --client-cert-name "peer-" +.br +--encryption-mode blowfish --hash-mode md5 --otp blowfish 3000 2000 +.RE +.SS "Example: Multiple local networks behind NATs, all connected to the Internet" +.P +For each peer in the existing local network, configure the NAT router to forward its +range of ports to it (assuming their port ranges do not overlap). The clients also need +to know the external IP address of the NAT router. If you don't have a static one, +you'll need to discover it before starting the clients. Also forward the server port to +the server. +.P +.B Starting the peers in the local network (other options as above): +.P +badvpn-client +.RS +.RB "..." +.br +--scope internet +.br +.RB "..." +.br +--ext-addr : internet +.br +.RB "..." +.RE +.P +The --ext-addr option applies to the previously specified --bind-addr option, and must come after +the first --ext-addr option which specifies a local address. +.P +Now perform a similar setup in some other local network behind a NAT. However: +.br +- Don't set up a new server, instead make the peers connect to the existing server in the first +local network. +.br +- You can't use {server_reported} for the local address --ext-addr options, because the server +would report the NAT router's external address rather than the peer's internal address. Instead +each peer has to know its internal IP address. +.br +- Use a different scope name for it, e.g. "local2" instead of "local1". +.P +If setup correctly, all peers will be able to communicate: those in the same local network will +communicate directly through local addresses, and those in different local networks will +communicate through the Internet. +.SH "PROTOCOL" +The protocols used in BadVPN are described in the source code in the protocol/ directory. +.SH "SEE ALSO" +.BR badvpn-server (8), +.BR badvpn-client (8) +.SH AUTHORS +Ambroz Bizjak diff --git a/app/src/main/jni/badvpn/base/BLog.c b/app/src/main/jni/badvpn/base/BLog.c new file mode 100644 index 0000000..e77073a --- /dev/null +++ b/app/src/main/jni/badvpn/base/BLog.c @@ -0,0 +1,88 @@ +/** + * @file BLog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "BLog.h" + +#ifdef ANDROID +#include +#endif + +#ifndef BADVPN_PLUGIN + +struct _BLog_channel blog_channel_list[] = { +#include +}; + +struct _BLog_global blog_global = { + #ifndef NDEBUG + 0 + #endif +}; + +#endif + +// keep in sync with level numbers in BLog.h! +static char *level_names[] = { NULL, "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" }; + +static void stdout_log (int channel, int level, const char *msg) +{ +#ifndef ANDROID + fprintf(stdout, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_DEBUG, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif +} + +static void stderr_log (int channel, int level, const char *msg) +{ +#ifndef ANDROID + fprintf(stderr, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_ERROR, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif +} + +static void stdout_stderr_free (void) +{ +} + +void BLog_InitStdout (void) +{ + BLog_Init(stdout_log, stdout_stderr_free); +} + +void BLog_InitStderr (void) +{ + BLog_Init(stderr_log, stdout_stderr_free); +} diff --git a/app/src/main/jni/badvpn/base/BLog.h b/app/src/main/jni/badvpn/base/BLog.h new file mode 100644 index 0000000..d7234ca --- /dev/null +++ b/app/src/main/jni/badvpn/base/BLog.h @@ -0,0 +1,399 @@ +/** + * @file BLog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for logging. + */ + +#ifndef BADVPN_BLOG_H +#define BADVPN_BLOG_H + +#include +#include + +#include +#include + +// auto-generated channel numbers and number of channels +#include + +// keep in sync with level names in BLog.c! +#define BLOG_ERROR 1 +#define BLOG_WARNING 2 +#define BLOG_NOTICE 3 +#define BLOG_INFO 4 +#define BLOG_DEBUG 5 + +#define BLog(...) BLog_LogToChannel(BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BContextLog(context, ...) BLog_ContextLog((context), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BLOG_CCCC(context) BLog_MakeChannelContext((context), BLOG_CURRENT_CHANNEL) + +typedef void (*_BLog_log_func) (int channel, int level, const char *msg); +typedef void (*_BLog_free_func) (void); + +struct _BLog_channel { + const char *name; + int loglevel; +}; + +struct _BLog_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + struct _BLog_channel channels[BLOG_NUM_CHANNELS]; + _BLog_log_func log_func; + _BLog_free_func free_func; + BMutex mutex; +#ifndef NDEBUG + int logging; +#endif + char logbuf[2048]; + int logbuf_pos; +}; + +extern struct _BLog_channel blog_channel_list[]; +extern struct _BLog_global blog_global; + +typedef void (*BLog_logfunc) (void *); + +typedef struct { + BLog_logfunc logfunc; + void *logfunc_user; +} BLogContext; + +typedef struct { + BLogContext context; + int channel; +} BLogChannelContext; + +static int BLogGlobal_GetChannelByName (const char *channel_name); + +static void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func); +static void BLog_Free (void); +static void BLog_SetChannelLoglevel (int channel, int loglevel); +static int BLog_WouldLog (int channel, int level); +static void BLog_Begin (void); +static void BLog_AppendVarArg (const char *fmt, va_list vl); +static void BLog_Append (const char *fmt, ...); +static void BLog_AppendBytes (const char *data, size_t len); +static void BLog_Finish (int channel, int level); +static void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl); +static void BLog_LogToChannel (int channel, int level, const char *fmt, ...); +static void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl); +static void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...); +static BLogContext BLog_RootContext (void); +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user); +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl); +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...); +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel); +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl); +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...); + +void BLog_InitStdout (void); +void BLog_InitStderr (void); + +int BLogGlobal_GetChannelByName (const char *channel_name) +{ + int i; + for (i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (!strcmp(blog_channel_list[i].name, channel_name)) { + return i; + } + } + + return -1; +} + +void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func) +{ + ASSERT(!blog_global.initialized) + + #ifndef NDEBUG + blog_global.initialized = 1; + #endif + + // initialize channels + memcpy(blog_global.channels, blog_channel_list, BLOG_NUM_CHANNELS * sizeof(struct _BLog_channel)); + + blog_global.log_func = log_func; + blog_global.free_func = free_func; +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + ASSERT_FORCE(BMutex_Init(&blog_global.mutex)) +} + +void BLog_Free (void) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(!blog_global.logging) +#endif + + BMutex_Free(&blog_global.mutex); + + #ifndef NDEBUG + blog_global.initialized = 0; + #endif + + blog_global.free_func(); +} + +void BLog_SetChannelLoglevel (int channel, int loglevel) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(loglevel >= 0 && loglevel <= BLOG_DEBUG) + + blog_global.channels[channel].loglevel = loglevel; +} + +int BLog_WouldLog (int channel, int level) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + return (level <= blog_global.channels[channel].loglevel); +} + +void BLog_Begin (void) +{ + ASSERT(blog_global.initialized) + + BMutex_Lock(&blog_global.mutex); + +#ifndef NDEBUG + ASSERT(!blog_global.logging) + blog_global.logging = 1; +#endif +} + +void BLog_AppendVarArg (const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + int w = vsnprintf(blog_global.logbuf + blog_global.logbuf_pos, sizeof(blog_global.logbuf) - blog_global.logbuf_pos, fmt, vl); + + if (w >= sizeof(blog_global.logbuf) - blog_global.logbuf_pos) { + blog_global.logbuf_pos = sizeof(blog_global.logbuf) - 1; + } else { + blog_global.logbuf_pos += w; + } +} + +void BLog_Append (const char *fmt, ...) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + + va_list vl; + va_start(vl, fmt); + BLog_AppendVarArg(fmt, vl); + va_end(vl); +} + +void BLog_AppendBytes (const char *data, size_t len) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + size_t avail = (sizeof(blog_global.logbuf) - 1) - blog_global.logbuf_pos; + len = (len > avail ? avail : len); + + memcpy(blog_global.logbuf + blog_global.logbuf_pos, data, len); + blog_global.logbuf_pos += len; + blog_global.logbuf[blog_global.logbuf_pos] = '\0'; +} + +void BLog_Finish (int channel, int level) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + ASSERT(BLog_WouldLog(channel, level)) + + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + ASSERT(blog_global.logbuf[blog_global.logbuf_pos] == '\0') + + blog_global.log_func(channel, level, blog_global.logbuf); + +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + BMutex_Unlock(&blog_global.mutex); +} + +void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogToChannel (int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +static void BLog__root_logfunc (void *unused) +{ +} + +static BLogContext BLog_RootContext (void) +{ + return BLog_MakeContext(BLog__root_logfunc, NULL); +} + +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user) +{ + ASSERT(logfunc) + + BLogContext context; + context.logfunc = logfunc; + context.logfunc_user = logfunc_user; + return context; +} + +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl) +{ + BLog_LogViaFuncVarArg(context.logfunc, context.logfunc_user, channel, level, fmt, vl); +} + +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ContextLogVarArg(context, channel, level, fmt, vl); + va_end(vl); +} + +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel) +{ + BLogChannelContext ccontext; + ccontext.context = context; + ccontext.channel = channel; + return ccontext; +} + +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl) +{ + BLog_ContextLogVarArg(ccontext.context, ccontext.channel, level, fmt, vl); +} + +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ChannelContextLogVarArg(ccontext, level, fmt, vl); + va_end(vl); +} + +#endif diff --git a/app/src/main/jni/badvpn/base/BLog_syslog.c b/app/src/main/jni/badvpn/base/BLog_syslog.c new file mode 100644 index 0000000..d7a954b --- /dev/null +++ b/app/src/main/jni/badvpn/base/BLog_syslog.c @@ -0,0 +1,150 @@ +/** + * @file BLog_syslog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include "BLog_syslog.h" + +static int resolve_facility (char *str, int *out) +{ + if (!strcmp(str, "authpriv")) { + *out = LOG_AUTHPRIV; + } + else if (!strcmp(str, "cron")) { + *out = LOG_CRON; + } + else if (!strcmp(str, "daemon")) { + *out = LOG_DAEMON; + } + else if (!strcmp(str, "ftp")) { + *out = LOG_FTP; + } + else if (!strcmp(str, "local0")) { + *out = LOG_LOCAL0; + } + else if (!strcmp(str, "local1")) { + *out = LOG_LOCAL1; + } + else if (!strcmp(str, "local2")) { + *out = LOG_LOCAL2; + } + else if (!strcmp(str, "local3")) { + *out = LOG_LOCAL3; + } + else if (!strcmp(str, "local4")) { + *out = LOG_LOCAL4; + } + else if (!strcmp(str, "local5")) { + *out = LOG_LOCAL5; + } + else if (!strcmp(str, "local6")) { + *out = LOG_LOCAL6; + } + else if (!strcmp(str, "local7")) { + *out = LOG_LOCAL7; + } + else if (!strcmp(str, "lpr")) { + *out = LOG_LPR; + } + else if (!strcmp(str, "mail")) { + *out = LOG_MAIL; + } + else if (!strcmp(str, "news")) { + *out = LOG_NEWS; + } + else if (!strcmp(str, "syslog")) { + *out = LOG_SYSLOG; + } + else if (!strcmp(str, "user")) { + *out = LOG_USER; + } + else if (!strcmp(str, "uucp")) { + *out = LOG_UUCP; + } + else { + return 0; + } + + return 1; +} + +static int convert_level (int level) +{ + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + switch (level) { + case BLOG_ERROR: + return LOG_ERR; + case BLOG_WARNING: + return LOG_WARNING; + case BLOG_NOTICE: + return LOG_NOTICE; + case BLOG_INFO: + return LOG_INFO; + case BLOG_DEBUG: + return LOG_DEBUG; + default: + ASSERT(0) + return 0; + } +} + +static struct { + char ident[200]; +} syslog_global; + +static void syslog_log (int channel, int level, const char *msg) +{ + syslog(convert_level(level), "%s: %s", blog_global.channels[channel].name, msg); +} + +static void syslog_free (void) +{ + closelog(); +} + +int BLog_InitSyslog (char *ident, char *facility_str) +{ + int facility; + if (!resolve_facility(facility_str, &facility)) { + return 0; + } + + snprintf(syslog_global.ident, sizeof(syslog_global.ident), "%s", ident); + + openlog(syslog_global.ident, 0, facility); + + BLog_Init(syslog_log, syslog_free); + + return 1; +} diff --git a/app/src/main/jni/badvpn/base/BLog_syslog.h b/app/src/main/jni/badvpn/base/BLog_syslog.h new file mode 100644 index 0000000..1adf04e --- /dev/null +++ b/app/src/main/jni/badvpn/base/BLog_syslog.h @@ -0,0 +1,42 @@ +/** + * @file BLog_syslog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * BLog syslog backend. + */ + +#ifndef BADVPN_BLOG_SYSLOG_H +#define BADVPN_BLOG_SYSLOG_H + +#include +#include + +int BLog_InitSyslog (char *ident, char *facility) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/base/BMutex.h b/app/src/main/jni/badvpn/base/BMutex.h new file mode 100644 index 0000000..fbcbd05 --- /dev/null +++ b/app/src/main/jni/badvpn/base/BMutex.h @@ -0,0 +1,101 @@ +/** + * @file BMutex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BMUTEX_H +#define BADVPN_BMUTEX_H + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +typedef struct { +#if BADVPN_THREAD_SAFE + pthread_mutex_t pthread_mutex; +#endif + DebugObject d_obj; +} BMutex; + +static int BMutex_Init (BMutex *o) WARN_UNUSED; +static void BMutex_Free (BMutex *o); +static void BMutex_Lock (BMutex *o); +static void BMutex_Unlock (BMutex *o); + +static int BMutex_Init (BMutex *o) +{ +#if BADVPN_THREAD_SAFE + if (pthread_mutex_init(&o->pthread_mutex, NULL) != 0) { + return 0; + } +#endif + + DebugObject_Init(&o->d_obj); + return 1; +} + +static void BMutex_Free (BMutex *o) +{ + DebugObject_Free(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_destroy(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Lock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_lock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Unlock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_unlock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +#endif diff --git a/app/src/main/jni/badvpn/base/BPending.c b/app/src/main/jni/badvpn/base/BPending.c new file mode 100644 index 0000000..6d05163 --- /dev/null +++ b/app/src/main/jni/badvpn/base/BPending.c @@ -0,0 +1,205 @@ +/** + * @file BPending.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "BPending.h" + +#include "BPending_list.h" +#include + +void BPendingGroup_Init (BPendingGroup *g) +{ + // init jobs list + BPending__List_Init(&g->jobs); + + // init pending counter + DebugCounter_Init(&g->pending_ctr); + + // init debug object + DebugObject_Init(&g->d_obj); +} + +void BPendingGroup_Free (BPendingGroup *g) +{ + DebugCounter_Free(&g->pending_ctr); + ASSERT(BPending__List_IsEmpty(&g->jobs)) + DebugObject_Free(&g->d_obj); +} + +int BPendingGroup_HasJobs (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return !BPending__List_IsEmpty(&g->jobs); +} + +void BPendingGroup_ExecuteJob (BPendingGroup *g) +{ + ASSERT(!BPending__List_IsEmpty(&g->jobs)) + DebugObject_Access(&g->d_obj); + + // get a job + BSmallPending *p = BPending__List_First(&g->jobs); + ASSERT(p->pending_node.next != p) + ASSERT(p->pending) + + // remove from jobs list + BPending__List_Remove(&g->jobs, p); + + // set not pending + p->pending_node.next = p; +#ifndef NDEBUG + p->pending = 0; +#endif + + // execute job + p->handler(p->user); + return; +} + +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return BPending__List_First(&g->jobs); +} + +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user) +{ + // init arguments + o->handler = handler; + o->user = user; + + // set not pending + o->pending_node.next = o; +#ifndef NDEBUG + o->pending = 0; +#endif + + // increment pending counter + DebugCounter_Increment(&g->pending_ctr); + + // init debug object + DebugObject_Init(&o->d_obj); +} + +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g) +{ + DebugCounter_Decrement(&g->pending_ctr); + DebugObject_Free(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + // remove from jobs list + if (o->pending_node.next != o) { + BPending__List_Remove(&g->jobs, o); + } +} + +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + // set handler + o->handler = handler; + o->user = user; +} + +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + // remove from jobs list + if (o->pending_node.next != o) { + BPending__List_Remove(&g->jobs, o); + } + + // insert to jobs list + BPending__List_Prepend(&g->jobs, o); + + // set pending +#ifndef NDEBUG + o->pending = 1; +#endif +} + +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + if (o->pending_node.next != o) { + // remove from jobs list + BPending__List_Remove(&g->jobs, o); + + // set not pending + o->pending_node.next = o; +#ifndef NDEBUG + o->pending = 0; +#endif + } +} + +int BSmallPending_IsSet (BSmallPending *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + return (o->pending_node.next != o); +} + +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user) +{ + BSmallPending_Init(&o->base, g, handler, user); + o->g = g; +} + +void BPending_Free (BPending *o) +{ + BSmallPending_Free(&o->base, o->g); +} + +void BPending_Set (BPending *o) +{ + BSmallPending_Set(&o->base, o->g); +} + +void BPending_Unset (BPending *o) +{ + BSmallPending_Unset(&o->base, o->g); +} + +int BPending_IsSet (BPending *o) +{ + return BSmallPending_IsSet(&o->base); +} diff --git a/app/src/main/jni/badvpn/base/BPending.h b/app/src/main/jni/badvpn/base/BPending.h new file mode 100644 index 0000000..07644be --- /dev/null +++ b/app/src/main/jni/badvpn/base/BPending.h @@ -0,0 +1,250 @@ +/** + * @file BPending.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for managing a queue of jobs pending execution. + */ + +#ifndef BADVPN_BPENDING_H +#define BADVPN_BPENDING_H + +#include + +#include +#include +#include + +struct BSmallPending_s; + +#include "BPending_list.h" +#include + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BSmallPending} object was + * in set state. + * The {@link BSmallPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BSmallPending_Init} + */ +typedef void (*BSmallPending_handler) (void *user); + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BPending} object was + * in set state. + * The {@link BPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BPending_Init} + */ +typedef void (*BPending_handler) (void *user); + +/** + * Object that contains a list of jobs pending execution. + */ +typedef struct { + BPending__List jobs; + DebugCounter pending_ctr; + DebugObject d_obj; +} BPendingGroup; + +/** + * Object for queuing a job for execution. + */ +typedef struct BSmallPending_s { + BPending_handler handler; + void *user; + BPending__ListNode pending_node; // optimization: if not pending, .next is this +#ifndef NDEBUG + uint8_t pending; +#endif + DebugObject d_obj; +} BSmallPending; + +/** + * Object for queuing a job for execution. This is a convenience wrapper + * around {@link BSmallPending} with an extra field to remember the + * {@link BPendingGroup} being used. + */ +typedef struct { + BSmallPending base; + BPendingGroup *g; +} BPending; + +/** + * Initializes the object. + * + * @param g the object + */ +void BPendingGroup_Init (BPendingGroup *g); + +/** + * Frees the object. + * There must be no {@link BPending} or {@link BSmallPending} objects using + * this group. + * + * @param g the object + */ +void BPendingGroup_Free (BPendingGroup *g); + +/** + * Checks if there is at least one job in the queue. + * + * @param g the object + * @return 1 if there is at least one job, 0 if not + */ +int BPendingGroup_HasJobs (BPendingGroup *g); + +/** + * Executes the top job on the job list. + * The job is removed from the list and enters + * not set state before being executed. + * There must be at least one job in job list. + * + * @param g the object + */ +void BPendingGroup_ExecuteJob (BPendingGroup *g); + +/** + * Returns the top job on the job list, or NULL if there are none. + * + * @param g the object + * @return the top job if there is at least one job, NULL if not + */ +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g); + +/** + * Changes the job execution handler. + * + * @param o the object + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BSmallPending_IsSet (BSmallPending *o); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + */ +void BPending_Free (BPending *o); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + */ +void BPending_Set (BPending *o); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + */ +void BPending_Unset (BPending *o); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BPending_IsSet (BPending *o); + +#endif diff --git a/app/src/main/jni/badvpn/base/BPending_list.h b/app/src/main/jni/badvpn/base/BPending_list.h new file mode 100644 index 0000000..eadac61 --- /dev/null +++ b/app/src/main/jni/badvpn/base/BPending_list.h @@ -0,0 +1,4 @@ +#define SLINKEDLIST_PARAM_NAME BPending__List +#define SLINKEDLIST_PARAM_FEATURE_LAST 0 +#define SLINKEDLIST_PARAM_TYPE_ENTRY struct BSmallPending_s +#define SLINKEDLIST_PARAM_MEMBER_NODE pending_node diff --git a/app/src/main/jni/badvpn/base/CMakeLists.txt b/app/src/main/jni/badvpn/base/CMakeLists.txt new file mode 100644 index 0000000..cf1f0f0 --- /dev/null +++ b/app/src/main/jni/badvpn/base/CMakeLists.txt @@ -0,0 +1,13 @@ +set(BASE_ADDITIONAL_SOURCES) + +if (HAVE_SYSLOG_H) + list(APPEND BASE_ADDITIONAL_SOURCES BLog_syslog.c) +endif () + +set(BASE_SOURCES + DebugObject.c + BLog.c + BPending.c + ${BASE_ADDITIONAL_SOURCES} +) +badvpn_add_library(base "" "" "${BASE_SOURCES}") diff --git a/app/src/main/jni/badvpn/base/DebugObject.c b/app/src/main/jni/badvpn/base/DebugObject.c new file mode 100644 index 0000000..e694617 --- /dev/null +++ b/app/src/main/jni/badvpn/base/DebugObject.c @@ -0,0 +1,39 @@ +/** + * @file DebugObject.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DebugObject.h" + +#ifndef BADVPN_PLUGIN +#ifndef NDEBUG +DebugCounter debugobject_counter = DEBUGCOUNTER_STATIC; +#if BADVPN_THREAD_SAFE +pthread_mutex_t debugobject_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +#endif +#endif diff --git a/app/src/main/jni/badvpn/base/DebugObject.h b/app/src/main/jni/badvpn/base/DebugObject.h new file mode 100644 index 0000000..b8db287 --- /dev/null +++ b/app/src/main/jni/badvpn/base/DebugObject.h @@ -0,0 +1,147 @@ +/** + * @file DebugObject.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used for detecting leaks. + */ + +#ifndef BADVPN_DEBUGOBJECT_H +#define BADVPN_DEBUGOBJECT_H + +#include + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +#define DEBUGOBJECT_VALID UINT32_C(0x31415926) + +/** + * Object used for detecting leaks. + */ +typedef struct { + #ifndef NDEBUG + uint32_t c; + #endif +} DebugObject; + +/** + * Initializes the object. + * + * @param obj the object + */ +static void DebugObject_Init (DebugObject *obj); + +/** + * Frees the object. + * + * @param obj the object + */ +static void DebugObject_Free (DebugObject *obj); + +/** + * Does nothing. + * + * @param obj the object + */ +static void DebugObject_Access (const DebugObject *obj); + +/** + * Does nothing. + * There must be no {@link DebugObject}'s initialized. + */ +static void DebugObjectGlobal_Finish (void); + +#ifndef NDEBUG +extern DebugCounter debugobject_counter; +#if BADVPN_THREAD_SAFE +extern pthread_mutex_t debugobject_mutex; +#endif +#endif + +void DebugObject_Init (DebugObject *obj) +{ + #ifndef NDEBUG + + obj->c = DEBUGOBJECT_VALID; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Increment(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Free (DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) + + #ifndef NDEBUG + + obj->c = 0; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Decrement(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Access (const DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) +} + +void DebugObjectGlobal_Finish (void) +{ + #ifndef NDEBUG + DebugCounter_Free(&debugobject_counter); + #endif +} + +#endif diff --git a/app/src/main/jni/badvpn/blog_channels.txt b/app/src/main/jni/badvpn/blog_channels.txt new file mode 100644 index 0000000..96313b5 --- /dev/null +++ b/app/src/main/jni/badvpn/blog_channels.txt @@ -0,0 +1,145 @@ +server 4 +client 4 +flooder 4 +tun2socks 4 +ncd 4 +ncd_var 4 +ncd_list 4 +ncd_depend 4 +ncd_multidepend 4 +ncd_dynamic_depend 4 +ncd_concat 4 +ncd_if 4 +ncd_strcmp 4 +ncd_regex_match 4 +ncd_logical 4 +ncd_sleep 4 +ncd_print 4 +ncd_blocker 4 +ncd_run 4 +ncd_runonce 4 +ncd_daemon 4 +ncd_spawn 4 +ncd_imperative 4 +ncd_ref 4 +ncd_index 4 +ncd_alias 4 +ncd_process_manager 4 +ncd_ondemand 4 +ncd_foreach 4 +ncd_choose 4 +ncd_net_backend_waitdevice 4 +ncd_net_backend_waitlink 4 +ncd_net_backend_badvpn 4 +ncd_net_backend_wpa_supplicant 4 +ncd_net_backend_rfkill 4 +ncd_net_up 4 +ncd_net_dns 4 +ncd_net_iptables 4 +ncd_net_ipv4_addr 4 +ncd_net_ipv4_route 4 +ncd_net_ipv4_dhcp 4 +ncd_net_ipv4_arp_probe 4 +ncd_net_watch_interfaces 4 +ncd_sys_watch_input 4 +ncd_sys_watch_usb 4 +ncd_sys_evdev 4 +ncd_sys_watch_directory 4 +StreamPeerIO 4 +DatagramPeerIO 4 +BReactor 3 +BSignal 3 +FragmentProtoAssembler 4 +BPredicate 3 +ServerConnection 4 +Listener 4 +DataProto 4 +FrameDecider 4 +BSocksClient 4 +BDHCPClientCore 4 +BDHCPClient 4 +NCDIfConfig 4 +BUnixSignal 4 +BProcess 4 +PRStreamSink 4 +PRStreamSource 4 +PacketProtoDecoder 4 +DPRelay 4 +BThreadWork 4 +DPReceive 4 +BInputProcess 4 +NCDUdevMonitorParser 4 +NCDUdevMonitor 4 +NCDUdevCache 4 +NCDUdevManager 4 +BTime 4 +BEncryption 4 +SPProtoDecoder 4 +LineBuffer 4 +BTap 4 +lwip 4 +NCDConfigTokenizer 4 +NCDConfigParser 4 +NCDValParser 4 +nsskey 4 +addr 4 +PasswordListener 4 +NCDInterfaceMonitor 4 +NCDRfkillMonitor 4 +udpgw 4 +UdpGwClient 4 +SocksUdpGwClient 4 +BNetwork 4 +BConnection 4 +BSSLConnection 4 +BDatagram 4 +PeerChat 4 +BArpProbe 4 +NCDModuleIndex 4 +NCDModuleProcess 4 +NCDValGenerator 4 +ncd_from_string 4 +ncd_to_string 4 +ncd_value 4 +ncd_try 4 +ncd_sys_request_server 4 +NCDRequest 4 +ncd_net_ipv6_wait_dynamic_addr 4 +NCDRequestClient 4 +ncd_request 4 +ncd_sys_request_client 4 +ncd_exit 4 +ncd_getargs 4 +ncd_arithmetic 4 +ncd_parse 4 +ncd_valuemetic 4 +ncd_file 4 +ncd_netmask 4 +ncd_implode 4 +ncd_call2 4 +ncd_assert 4 +ncd_reboot 4 +ncd_explode 4 +NCDPlaceholderDb 4 +NCDVal 4 +ncd_net_ipv6_addr 4 +ncd_net_ipv6_route 4 +ncd_net_ipv4_addr_in_network 4 +ncd_net_ipv6_addr_in_network 4 +dostest_server 4 +dostest_attacker 4 +ncd_timer 4 +ncd_file_open 4 +ncd_backtrack 4 +ncd_socket 4 +ncd_depend_scope 4 +ncd_substr 4 +ncd_sys_start_process 4 +NCDBuildProgram 4 +ncd_log 4 +ncd_log_msg 4 +ncd_buffer 4 +ncd_getenv 4 +BThreadSignal 4 +BLockReactor 4 +ncd_load_module 4 diff --git a/app/src/main/jni/badvpn/blog_generator/blog.php b/app/src/main/jni/badvpn/blog_generator/blog.php new file mode 100644 index 0000000..fd436bc --- /dev/null +++ b/app/src/main/jni/badvpn/blog_generator/blog.php @@ -0,0 +1,121 @@ + Input channels file. + --output-dir Destination directory for generated files. + +EOD; +} + +$input_file = ""; +$output_dir = ""; + +for ($i = 1; $i < $argc;) { + $arg = $argv[$i++]; + switch ($arg) { + case "--input-file": + $input_file = $argv[$i++]; + break; + case "--output-dir": + $output_dir = $argv[$i++]; + break; + case "--help": + print_help($argv[0]); + exit(0); + default: + fatal_error("Unknown option: {$arg}"); + } +} + +if ($input_file == "") { + fatal_error("--input-file missing"); +} + +if ($output_dir == "") { + fatal_error("--output-dir missing"); +} + +if (($data = file_get_contents($input_file)) === FALSE) { + fatal_error("Failed to read input file"); +} + +if (!tokenize($data, $tokens)) { + fatal_error("Failed to tokenize"); +} + +$i = 0; +$channels_defines = ""; +$channels_list = ""; + +reset($tokens); + +while (1) { + if (($ch_name = current($tokens)) === FALSE) { + break; + } + next($tokens); + if (($ch_priority = current($tokens)) === FALSE) { + fatal_error("missing priority"); + } + next($tokens); + if ($ch_name[0] != "name") { + fatal_error("name is not a name"); + } + if ($ch_priority[0] != "number") { + fatal_error("priority is not a number"); + } + + $channel_file = << 0) { + if (preg_match('/^\\/\\/.*/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\\s+/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[0-9]+/', $str, $matches)) { + $out[] = array('number', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) { + $out[] = array('name', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else { + return FALSE; + } + } + + return TRUE; +} + +function fatal_error ($message) +{ + fwrite(STDERR, "Fatal error: $message\n"); + + ob_get_clean(); + exit(1); +} diff --git a/app/src/main/jni/badvpn/bproto/BProto.h b/app/src/main/jni/badvpn/bproto/BProto.h new file mode 100644 index 0000000..5f2a696 --- /dev/null +++ b/app/src/main/jni/badvpn/bproto/BProto.h @@ -0,0 +1,85 @@ +/** + * @file BProto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for BProto serialization. + */ + +#ifndef BADVPN_BPROTO_BPROTO_H +#define BADVPN_BPROTO_BPROTO_H + +#include + +#include + +#define BPROTO_TYPE_UINT8 1 +#define BPROTO_TYPE_UINT16 2 +#define BPROTO_TYPE_UINT32 3 +#define BPROTO_TYPE_UINT64 4 +#define BPROTO_TYPE_DATA 5 +#define BPROTO_TYPE_CONSTDATA 6 + +B_START_PACKED +struct BProto_header_s { + uint16_t id; + uint16_t type; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint8_s { + uint8_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint16_s { + uint16_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint32_s { + uint32_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint64_s { + uint64_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_data_header_s { + uint32_t len; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/bproto_generator/ProtoParser.lime b/app/src/main/jni/badvpn/bproto_generator/ProtoParser.lime new file mode 100644 index 0000000..f039e1d --- /dev/null +++ b/app/src/main/jni/badvpn/bproto_generator/ProtoParser.lime @@ -0,0 +1,99 @@ +%class ProtoParser +%start file + +file = + directives messages { + $$ = array( + "directives" => $1, + "messages" => $2 + ); + }. + +directives = + { + $$ = array(); + } | + directive semicolon directives { + $$ = array_merge(array($1), $3); + }. + +directive = + include string { + $$ = array( + "type" => "include", + "file" => $2 + ); + }. + +messages = + msgspec { + $$ = array($1); + } | + msgspec messages { + $$ = array_merge(array($1), $2); + }. + +msgspec = + message name spar entries epar semicolon { + $$ = array( + "name" => $2, + "entries" => $4 + ); +}. + +entries = + entry { + $$ = array($1); + } | + entry entries { + $$ = array_merge(array($1), $2); + }. + +entry = + cardinality type name equals number semicolon { + $$ = array( + "cardinality" => $1, + "type" => $2, + "name" => $3, + "id" => $5 + ); + }. + +cardinality = + repeated { + $$ = "repeated"; + } | + optional { + $$ = "optional"; + } | + required { + $$ = "required"; + } | + required repeated { + $$ = "required repeated"; + }. + +type = + uint { + $$ = array( + "type" => "uint", + "size" => $1 + ); + } | + data { + $$ = array( + "type" => "data" + ); + } | + data srpar string erpar { + $$ = array( + "type" => "constdata", + "size" => $3 + ); + } | + message name { + $$ = array( + "type" => "message", + "message" => $2 + ); + }. diff --git a/app/src/main/jni/badvpn/bproto_generator/ProtoParser.php b/app/src/main/jni/badvpn/bproto_generator/ProtoParser.php new file mode 100644 index 0000000..0477dba --- /dev/null +++ b/app/src/main/jni/badvpn/bproto_generator/ProtoParser.php @@ -0,0 +1,560 @@ + + array ( + 'directives' => 's 1', + 'directive' => 's 30', + 'include' => 's 33', + 'file' => 's 35', + '\'start\'' => 'a \'start\'', + 'message' => 'r 1', + ), + 1 => + array ( + 'messages' => 's 2', + 'msgspec' => 's 3', + 'message' => 's 5', + ), + 2 => + array ( + '#' => 'r 0', + ), + 3 => + array ( + 'msgspec' => 's 3', + 'messages' => 's 4', + 'message' => 's 5', + '#' => 'r 4', + ), + 4 => + array ( + '#' => 'r 5', + ), + 5 => + array ( + 'name' => 's 6', + ), + 6 => + array ( + 'spar' => 's 7', + ), + 7 => + array ( + 'entries' => 's 8', + 'entry' => 's 11', + 'cardinality' => 's 13', + 'repeated' => 's 26', + 'optional' => 's 27', + 'required' => 's 28', + ), + 8 => + array ( + 'epar' => 's 9', + ), + 9 => + array ( + 'semicolon' => 's 10', + ), + 10 => + array ( + 'message' => 'r 6', + '#' => 'r 6', + ), + 11 => + array ( + 'entry' => 's 11', + 'entries' => 's 12', + 'cardinality' => 's 13', + 'repeated' => 's 26', + 'optional' => 's 27', + 'required' => 's 28', + 'epar' => 'r 7', + ), + 12 => + array ( + 'epar' => 'r 8', + ), + 13 => + array ( + 'type' => 's 14', + 'uint' => 's 19', + 'data' => 's 20', + 'message' => 's 24', + ), + 14 => + array ( + 'name' => 's 15', + ), + 15 => + array ( + 'equals' => 's 16', + ), + 16 => + array ( + 'number' => 's 17', + ), + 17 => + array ( + 'semicolon' => 's 18', + ), + 18 => + array ( + 'repeated' => 'r 9', + 'optional' => 'r 9', + 'required' => 'r 9', + 'epar' => 'r 9', + ), + 19 => + array ( + 'name' => 'r 14', + ), + 20 => + array ( + 'srpar' => 's 21', + 'name' => 'r 15', + ), + 21 => + array ( + 'string' => 's 22', + ), + 22 => + array ( + 'erpar' => 's 23', + ), + 23 => + array ( + 'name' => 'r 16', + ), + 24 => + array ( + 'name' => 's 25', + ), + 25 => + array ( + 'name' => 'r 17', + ), + 26 => + array ( + 'uint' => 'r 10', + 'data' => 'r 10', + 'message' => 'r 10', + ), + 27 => + array ( + 'uint' => 'r 11', + 'data' => 'r 11', + 'message' => 'r 11', + ), + 28 => + array ( + 'repeated' => 's 29', + 'uint' => 'r 12', + 'data' => 'r 12', + 'message' => 'r 12', + ), + 29 => + array ( + 'uint' => 'r 13', + 'data' => 'r 13', + 'message' => 'r 13', + ), + 30 => + array ( + 'semicolon' => 's 31', + ), + 31 => + array ( + 'directive' => 's 30', + 'directives' => 's 32', + 'include' => 's 33', + 'message' => 'r 1', + ), + 32 => + array ( + 'message' => 'r 2', + ), + 33 => + array ( + 'string' => 's 34', + ), + 34 => + array ( + 'semicolon' => 'r 3', + ), + 35 => + array ( + '#' => 'r 18', + ), +); +function reduce_0_file_1($tokens, &$result) { +# +# (0) file := directives messages +# +$result = reset($tokens); + + $result = array( + "directives" => $tokens[0], + "messages" => $tokens[1] + ); + +} + +function reduce_1_directives_1($tokens, &$result) { +# +# (1) directives := +# +$result = reset($tokens); + + $result = array(); + +} + +function reduce_2_directives_2($tokens, &$result) { +# +# (2) directives := directive semicolon directives +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[2]); + +} + +function reduce_3_directive_1($tokens, &$result) { +# +# (3) directive := include string +# +$result = reset($tokens); + + $result = array( + "type" => "include", + "file" => $tokens[1] + ); + +} + +function reduce_4_messages_1($tokens, &$result) { +# +# (4) messages := msgspec +# +$result = reset($tokens); + + $result = array($tokens[0]); + +} + +function reduce_5_messages_2($tokens, &$result) { +# +# (5) messages := msgspec messages +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[1]); + +} + +function reduce_6_msgspec_1($tokens, &$result) { +# +# (6) msgspec := message name spar entries epar semicolon +# +$result = reset($tokens); + + $result = array( + "name" => $tokens[1], + "entries" => $tokens[3] + ); + +} + +function reduce_7_entries_1($tokens, &$result) { +# +# (7) entries := entry +# +$result = reset($tokens); + + $result = array($tokens[0]); + +} + +function reduce_8_entries_2($tokens, &$result) { +# +# (8) entries := entry entries +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[1]); + +} + +function reduce_9_entry_1($tokens, &$result) { +# +# (9) entry := cardinality type name equals number semicolon +# +$result = reset($tokens); + + $result = array( + "cardinality" => $tokens[0], + "type" => $tokens[1], + "name" => $tokens[2], + "id" => $tokens[4] + ); + +} + +function reduce_10_cardinality_1($tokens, &$result) { +# +# (10) cardinality := repeated +# +$result = reset($tokens); + + $result = "repeated"; + +} + +function reduce_11_cardinality_2($tokens, &$result) { +# +# (11) cardinality := optional +# +$result = reset($tokens); + + $result = "optional"; + +} + +function reduce_12_cardinality_3($tokens, &$result) { +# +# (12) cardinality := required +# +$result = reset($tokens); + + $result = "required"; + +} + +function reduce_13_cardinality_4($tokens, &$result) { +# +# (13) cardinality := required repeated +# +$result = reset($tokens); + + $result = "required repeated"; + +} + +function reduce_14_type_1($tokens, &$result) { +# +# (14) type := uint +# +$result = reset($tokens); + + $result = array( + "type" => "uint", + "size" => $tokens[0] + ); + +} + +function reduce_15_type_2($tokens, &$result) { +# +# (15) type := data +# +$result = reset($tokens); + + $result = array( + "type" => "data" + ); + +} + +function reduce_16_type_3($tokens, &$result) { +# +# (16) type := data srpar string erpar +# +$result = reset($tokens); + + $result = array( + "type" => "constdata", + "size" => $tokens[2] + ); + +} + +function reduce_17_type_4($tokens, &$result) { +# +# (17) type := message name +# +$result = reset($tokens); + + $result = array( + "type" => "message", + "message" => $tokens[1] + ); + +} + +function reduce_18_start_1($tokens, &$result) { +# +# (18) 'start' := file +# +$result = reset($tokens); + +} + +var $method = array ( + 0 => 'reduce_0_file_1', + 1 => 'reduce_1_directives_1', + 2 => 'reduce_2_directives_2', + 3 => 'reduce_3_directive_1', + 4 => 'reduce_4_messages_1', + 5 => 'reduce_5_messages_2', + 6 => 'reduce_6_msgspec_1', + 7 => 'reduce_7_entries_1', + 8 => 'reduce_8_entries_2', + 9 => 'reduce_9_entry_1', + 10 => 'reduce_10_cardinality_1', + 11 => 'reduce_11_cardinality_2', + 12 => 'reduce_12_cardinality_3', + 13 => 'reduce_13_cardinality_4', + 14 => 'reduce_14_type_1', + 15 => 'reduce_15_type_2', + 16 => 'reduce_16_type_3', + 17 => 'reduce_17_type_4', + 18 => 'reduce_18_start_1', +); +var $a = array ( + 0 => + array ( + 'symbol' => 'file', + 'len' => 2, + 'replace' => true, + ), + 1 => + array ( + 'symbol' => 'directives', + 'len' => 0, + 'replace' => true, + ), + 2 => + array ( + 'symbol' => 'directives', + 'len' => 3, + 'replace' => true, + ), + 3 => + array ( + 'symbol' => 'directive', + 'len' => 2, + 'replace' => true, + ), + 4 => + array ( + 'symbol' => 'messages', + 'len' => 1, + 'replace' => true, + ), + 5 => + array ( + 'symbol' => 'messages', + 'len' => 2, + 'replace' => true, + ), + 6 => + array ( + 'symbol' => 'msgspec', + 'len' => 6, + 'replace' => true, + ), + 7 => + array ( + 'symbol' => 'entries', + 'len' => 1, + 'replace' => true, + ), + 8 => + array ( + 'symbol' => 'entries', + 'len' => 2, + 'replace' => true, + ), + 9 => + array ( + 'symbol' => 'entry', + 'len' => 6, + 'replace' => true, + ), + 10 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 11 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 12 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 13 => + array ( + 'symbol' => 'cardinality', + 'len' => 2, + 'replace' => true, + ), + 14 => + array ( + 'symbol' => 'type', + 'len' => 1, + 'replace' => true, + ), + 15 => + array ( + 'symbol' => 'type', + 'len' => 1, + 'replace' => true, + ), + 16 => + array ( + 'symbol' => 'type', + 'len' => 4, + 'replace' => true, + ), + 17 => + array ( + 'symbol' => 'type', + 'len' => 2, + 'replace' => true, + ), + 18 => + array ( + 'symbol' => '\'start\'', + 'len' => 1, + 'replace' => true, + ), +); +} diff --git a/app/src/main/jni/badvpn/bproto_generator/bproto.php b/app/src/main/jni/badvpn/bproto_generator/bproto.php new file mode 100644 index 0000000..76f8c6e --- /dev/null +++ b/app/src/main/jni/badvpn/bproto_generator/bproto.php @@ -0,0 +1,115 @@ + + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +require_once "lime/parse_engine.php"; +require_once "ProtoParser.php"; +require_once "bproto_functions.php"; + +function assert_failure ($script, $line, $message) +{ + if ($message == "") { + fatal_error("Assertion failure at {$script}:{$line}"); + } else { + fatal_error("Assertion failure at {$script}:{$line}: {$message}"); + } +} + +assert_options(ASSERT_CALLBACK, "assert_failure"); + +function print_help ($name) +{ + echo << Output file prefix. + --input-file Message file to generate source for. + --output-dir Destination directory for generated files. + +EOD; +} + +$name = ""; +$input_file = ""; +$output_dir = ""; + +for ($i = 1; $i < $argc;) { + $arg = $argv[$i++]; + switch ($arg) { + case "--name": + $name = $argv[$i++]; + break; + case "--input-file": + $input_file = $argv[$i++]; + break; + case "--output-dir": + $output_dir = $argv[$i++]; + break; + case "--help": + print_help($argv[0]); + exit(0); + default: + fatal_error("Unknown option: {$arg}"); + } +} + +if ($name == "") { + fatal_error("--name missing"); +} + +if ($input_file == "") { + fatal_error("--input-file missing"); +} + +if ($output_dir == "") { + fatal_error("--output-dir missing"); +} + +if (($data = file_get_contents($input_file)) === FALSE) { + fatal_error("Failed to read input file"); +} + +if (!tokenize($data, $tokens)) { + fatal_error("Failed to tokenize"); +} + +$parser = new parse_engine(new ProtoParser()); + +try { + foreach ($tokens as $token) { + $parser->eat($token[0], $token[1]); + } + $parser->eat_eof(); +} catch (parse_error $e) { + fatal_error("$input_file: Parse error: ".$e->getMessage()); +} + +$data = generate_header($name, $parser->semantic["directives"], $parser->semantic["messages"]); +if (file_put_contents("{$output_dir}/{$name}.h", $data) === NULL) { + fatal_error("{$input_file}: Failed to write .h file"); +} diff --git a/app/src/main/jni/badvpn/bproto_generator/bproto_functions.php b/app/src/main/jni/badvpn/bproto_generator/bproto_functions.php new file mode 100644 index 0000000..490c1bf --- /dev/null +++ b/app/src/main/jni/badvpn/bproto_generator/bproto_functions.php @@ -0,0 +1,777 @@ + 0) { + if (preg_match('/^\\/\\/.*/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\\s+/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^include/', $str, $matches)) { + $out[] = array('include', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^message/', $str, $matches)) { + $out[] = array('message', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^repeated/', $str, $matches)) { + $out[] = array('repeated', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^required/', $str, $matches)) { + $out[] = array('required', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^optional/', $str, $matches)) { + $out[] = array('optional', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^{/', $str, $matches)) { + $out[] = array('spar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^}/', $str, $matches)) { + $out[] = array('epar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\(/', $str, $matches)) { + $out[] = array('srpar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\)/', $str, $matches)) { + $out[] = array('erpar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^=/', $str, $matches)) { + $out[] = array('equals', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^;/', $str, $matches)) { + $out[] = array('semicolon', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^uint(8|16|32|64)/', $str, $matches)) { + $out[] = array('uint', $matches[1]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^data/', $str, $matches)) { + $out[] = array('data', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[0-9]+/', $str, $matches)) { + $out[] = array('number', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) { + $out[] = array('name', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^"([^"]*)"/', $str, $matches)) { + $out[] = array('string', $matches[1]); + $str = substr($str, strlen($matches[0])); + } + else { + return FALSE; + } + } + + return TRUE; +} + +function fatal_error ($message) +{ + fwrite(STDERR, "Fatal error: $message\n"); + + ob_get_clean(); + exit(1); +} + +function make_writer_decl ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "void {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, uint{$entry["type"]["size"]}_t v)"; + case "data": + return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, int len)"; + case "constdata": + return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o)"; + default: + assert(0); + } +} + +function make_parser_decl ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint{$entry["type"]["size"]}_t *v)"; + case "data": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data, int *data_len)"; + case "constdata": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data)"; + default: + assert(0); + } +} + +function make_parser_reset_decl ($msg, $entry) +{ + return "void {$msg["name"]}Parser_Reset{$entry["name"]} ({$msg["name"]}Parser *o)"; +} + +function make_parser_forward_decl ($msg, $entry) +{ + return "void {$msg["name"]}Parser_Forward{$entry["name"]} ({$msg["name"]}Parser *o)"; +} + +function make_type_name ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "BPROTO_TYPE_UINT{$entry["type"]["size"]}"; + case "data": + return "BPROTO_TYPE_DATA"; + case "constdata": + return "BPROTO_TYPE_CONSTDATA"; + default: + assert(0); + } +} + +function make_finish_assert ($msg, $entry) +{ + switch ($entry["cardinality"]) { + case "repeated": + return "ASSERT(o->{$entry["name"]}_count >= 0)"; + case "required repeated": + return "ASSERT(o->{$entry["name"]}_count >= 1)"; + case "optional": + return "ASSERT(o->{$entry["name"]}_count >= 0 && o->{$entry["name"]}_count <= 1)"; + case "required": + return "ASSERT(o->{$entry["name"]}_count == 1)"; + default: + assert(0); + } +} + +function make_add_count_assert ($msg, $entry) +{ + if (in_array($entry["cardinality"], array("optional", "required"))) { + return "ASSERT(o->{$entry["name"]}_count == 0)"; + } + return ""; +} + +function make_add_length_assert ($msg, $entry) +{ + if ($entry["type"]["type"] == "data") { + return "ASSERT(len >= 0 && len <= UINT32_MAX)"; + } + return ""; +} + +function make_size_define ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint{$entry["type"]["size"]}_s))"; + case "data": + return "#define {$msg["name"]}_SIZE{$entry["name"]}(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len))"; + case "constdata": + return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + ({$entry["type"]["size"]}))"; + default: + assert(0); + } +} + +function generate_header ($name, $directives, $messages) { + ob_start(); + + echo << +#include + +#include +#include +#include + + +EOD; + + foreach ($directives as $directive) { + if ($directive["type"] == "include") { + echo <<out = out; + o->used = 0; + +EOD; + + foreach ($msg["entries"] as $entry) { + echo <<{$entry["name"]}_count = 0; + +EOD; + } + + echo <<used >= 0) + +EOD; + + foreach ($msg["entries"] as $entry) { + $ass = make_finish_assert($msg, $entry); + echo <<used; +} + + +EOD; + + foreach ($msg["entries"] as $entry) { + $decl = make_writer_decl($msg, $entry); + $type = make_type_name($msg, $entry); + $add_count_assert = make_add_count_assert($msg, $entry); + $add_length_assert = make_add_length_assert($msg, $entry); + + echo <<used >= 0) + {$add_count_assert} + {$add_length_assert} + + struct BProto_header_s header; + header.id = htol16({$entry["id"]}); + header.type = htol16({$type}); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + +EOD; + switch ($entry["type"]["type"]) { + case "uint": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s); + +EOD; + break; + case "data": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + +EOD; + break; + case "constdata": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += ({$entry["type"]["size"]}); + +EOD; + break; + default: + assert(0); + } + + echo <<{$entry["name"]}_count++; + +EOD; + if (in_array($entry["type"]["type"], array("data", "constdata"))) { + echo <<= 0) + + o->buf = buf; + o->buf_len = buf_len; + +EOD; + + foreach ($msg["entries"] as $entry) { + echo <<{$entry["name"]}_start = o->buf_len; + o->{$entry["name"]}_span = 0; + o->{$entry["name"]}_pos = 0; + +EOD; + } + + echo <<buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + +EOD; + + foreach (array(8, 16, 32, 64) as $bits) { + echo <<= sizeof(struct BProto_uint{$bits}_s))) { + return 0; + } + pos += sizeof(struct BProto_uint{$bits}_s); + left -= sizeof(struct BProto_uint{$bits}_s); + + switch (id) { + +EOD; + + foreach ($msg["entries"] as $entry) { + if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) { + continue; + } + $type = make_type_name($msg, $entry); + echo <<{$entry["name"]}_start == o->buf_len) { + o->{$entry["name"]}_start = entry_pos; + } + o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start; + {$entry["name"]}_count++; + break; + +EOD; + } + + echo <<= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + +EOD; + + foreach ($msg["entries"] as $entry) { + if (!in_array($entry["type"]["type"], array("data", "constdata"))) { + continue; + } + $type = make_type_name($msg, $entry); + echo <<{$entry["name"]}_start == o->buf_len) { + o->{$entry["name"]}_start = entry_pos; + } + o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start; + {$entry["name"]}_count++; + break; + +EOD; + } + + echo <<= 1"; + break; + case "optional": + $cond = "{$entry["name"]}_count <= 1"; + break; + case "required": + $cond = "{$entry["name"]}_count == 1"; + break; + default: + assert(0); + } + if ($cond) { + echo <<{$entry["name"]}_pos == o->{$entry["name"]}_span + +EOD; + } + + + echo <<{$entry["name"]}_pos >= 0) + ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span) + + int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(header)); + o->{$entry["name"]}_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + +EOD; + + foreach (array(8, 16, 32, 64) as $bits) { + echo <<= sizeof(struct BProto_uint{$bits}_s)) + +EOD; + if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) { + echo <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val)); + +EOD; + } + echo <<{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s); + left -= sizeof(struct BProto_uint{$bits}_s); + +EOD; + if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) { + echo <<= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val)); + o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + +EOD; + if ($entry["type"]["type"] == "data" || $entry["type"]["type"] == "constdata") { + echo <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos; + +EOD; + } + echo <<{$entry["name"]}_pos += payload_len; + left -= payload_len; + +EOD; + if ($entry["type"]["type"] == "data") { + echo <<{$entry["name"]}_pos = 0; +} + +{$forward_decl} +{ + o->{$entry["name"]}_pos = o->{$entry["name"]}_span; +} + + +EOD; + } + } + + return ob_get_clean(); +} diff --git a/app/src/main/jni/badvpn/client/CMakeLists.txt b/app/src/main/jni/badvpn/client/CMakeLists.txt new file mode 100644 index 0000000..3cec1a9 --- /dev/null +++ b/app/src/main/jni/badvpn/client/CMakeLists.txt @@ -0,0 +1,30 @@ +add_executable(badvpn-client + client.c + StreamPeerIO.c + DatagramPeerIO.c + PasswordListener.c + DataProto.c + FrameDecider.c + DPRelay.c + DPReceive.c + FragmentProtoDisassembler.c + FragmentProtoAssembler.c + SPProtoEncoder.c + SPProtoDecoder.c + DataProtoKeepaliveSource.c + PeerChat.c + SCOutmsgEncoder.c + SimpleStreamBuffer.c + SinglePacketSource.c +) +target_link_libraries(badvpn-client system flow flowextra tuntap server_conection security threadwork ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-client + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-client.8 + DESTINATION share/man/man8 +) diff --git a/app/src/main/jni/badvpn/client/DPReceive.c b/app/src/main/jni/badvpn/client/DPReceive.c new file mode 100644 index 0000000..da70c74 --- /dev/null +++ b/app/src/main/jni/badvpn/client/DPReceive.c @@ -0,0 +1,324 @@ +/** + * @file DPReceive.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static DPReceivePeer * find_peer (DPReceiveDevice *o, peerid_t id) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->peers_list); node; node = LinkedList1Node_Next(node)) { + DPReceivePeer *p = UPPER_OBJECT(node, DPReceivePeer, list_node); + if (p->peer_id == id) { + return p; + } + } + + return NULL; +} + +static void receiver_recv_handler_send (DPReceiveReceiver *o, uint8_t *packet, int packet_len) +{ + DebugObject_Access(&o->d_obj); + DPReceivePeer *peer = o->peer; + DPReceiveDevice *device = peer->device; + ASSERT(packet_len >= 0) + ASSERT(packet_len <= device->packet_mtu) + + uint8_t *data = packet; + int data_len = packet_len; + + int local = 0; + DPReceivePeer *src_peer; + DPReceivePeer *relay_dest_peer = NULL; + + // check header + if (data_len < sizeof(struct dataproto_header)) { + BLog(BLOG_WARNING, "no dataproto header"); + goto out; + } + struct dataproto_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + peerid_t from_id = ltoh16(header.from_id); + int num_ids = ltoh16(header.num_peer_ids); + + // check destination ID + if (!(num_ids == 0 || num_ids == 1)) { + BLog(BLOG_WARNING, "wrong number of destinations"); + goto out; + } + peerid_t to_id = 0; // to remove warning + if (num_ids == 1) { + if (data_len < sizeof(struct dataproto_peer_id)) { + BLog(BLOG_WARNING, "missing destination"); + goto out; + } + struct dataproto_peer_id id; + memcpy(&id, data, sizeof(id)); + to_id = ltoh16(id.id); + data += sizeof(id); + data_len -= sizeof(id); + } + + // check remaining data + if (data_len > device->device_mtu) { + BLog(BLOG_WARNING, "frame too large"); + goto out; + } + + // inform sink of received packet + if (peer->dp_sink) { + DataProtoSink_Received(peer->dp_sink, !!(flags & DATAPROTO_FLAGS_RECEIVING_KEEPALIVES)); + } + + if (num_ids == 1) { + // find source peer + if (!(src_peer = find_peer(device, from_id))) { + BLog(BLOG_INFO, "source peer %d not known", (int)from_id); + goto out; + } + + // is frame for device or another peer? + if (device->have_peer_id && to_id == device->peer_id) { + // let the frame decider analyze the frame + FrameDeciderPeer_Analyze(src_peer->decider_peer, data, data_len); + + // pass frame to device + local = 1; + } else { + // check if relaying is allowed + if (!peer->is_relay_client) { + BLog(BLOG_WARNING, "relaying not allowed"); + goto out; + } + + // provided source ID must be the peer sending the frame + if (src_peer != peer) { + BLog(BLOG_WARNING, "relay source must be the sending peer"); + goto out; + } + + // find destination peer + DPReceivePeer *dest_peer = find_peer(device, to_id); + if (!dest_peer) { + BLog(BLOG_INFO, "relay destination peer not known"); + goto out; + } + + // destination cannot be source + if (dest_peer == src_peer) { + BLog(BLOG_WARNING, "relay destination cannot be the source"); + goto out; + } + + relay_dest_peer = dest_peer; + } + } + +out: + // accept packet + PacketPassInterface_Done(&o->recv_if); + + // pass packet to device + if (local) { + o->device->output_func(o->device->output_func_user, data, data_len); + } + + // relay frame + if (relay_dest_peer) { + DPRelayRouter_SubmitFrame(&device->relay_router, &src_peer->relay_source, &relay_dest_peer->relay_sink, data, data_len, device->relay_flow_buffer_size, device->relay_flow_inactivity_time); + } +} + +int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time) +{ + ASSERT(device_mtu >= 0) + ASSERT(device_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + ASSERT(output_func) + ASSERT(relay_flow_buffer_size > 0) + + // init arguments + o->device_mtu = device_mtu; + o->output_func = output_func; + o->output_func_user = output_func_user; + o->reactor = reactor; + o->relay_flow_buffer_size = relay_flow_buffer_size; + o->relay_flow_inactivity_time = relay_flow_inactivity_time; + + // remember packet MTU + o->packet_mtu = DATAPROTO_MAX_OVERHEAD + o->device_mtu; + + // init relay router + if (!DPRelayRouter_Init(&o->relay_router, o->device_mtu, o->reactor)) { + BLog(BLOG_ERROR, "DPRelayRouter_Init failed"); + goto fail0; + } + + // have no peer ID + o->have_peer_id = 0; + + // init peers list + LinkedList1_Init(&o->peers_list); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void DPReceiveDevice_Free (DPReceiveDevice *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(LinkedList1_IsEmpty(&o->peers_list)) + + // free relay router + DPRelayRouter_Free(&o->relay_router); +} + +void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id) +{ + DebugObject_Access(&o->d_obj); + + // remember peer ID + o->peer_id = peer_id; + o->have_peer_id = 1; +} + +void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client) +{ + DebugObject_Access(&device->d_obj); + ASSERT(is_relay_client == 0 || is_relay_client == 1) + + // init arguments + o->device = device; + o->peer_id = peer_id; + o->decider_peer = decider_peer; + o->is_relay_client = is_relay_client; + + // init relay source + DPRelaySource_Init(&o->relay_source, &device->relay_router, o->peer_id, device->reactor); + + // init relay sink + DPRelaySink_Init(&o->relay_sink, o->peer_id); + + // have no sink + o->dp_sink = NULL; + + // insert to peers list + LinkedList1_Append(&device->peers_list, &o->list_node); + + DebugCounter_Init(&o->d_receivers_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPReceivePeer_Free (DPReceivePeer *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_receivers_ctr); + ASSERT(!o->dp_sink) + + // remove from peers list + LinkedList1_Remove(&o->device->peers_list, &o->list_node); + + // free relay sink + DPRelaySink_Free(&o->relay_sink); + + // free relay source + DPRelaySource_Free(&o->relay_source); +} + +void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->dp_sink) + ASSERT(dp_sink) + + // attach relay sink + DPRelaySink_Attach(&o->relay_sink, dp_sink); + + o->dp_sink = dp_sink; +} + +void DPReceivePeer_DetachSink (DPReceivePeer *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->dp_sink) + + // detach relay sink + DPRelaySink_Detach(&o->relay_sink); + + o->dp_sink = NULL; +} + +void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer) +{ + DebugObject_Access(&peer->d_obj); + DPReceiveDevice *device = peer->device; + + // init arguments + o->peer = peer; + + // remember device + o->device = device; + + // init receive interface + PacketPassInterface_Init(&o->recv_if, device->packet_mtu, (PacketPassInterface_handler_send)receiver_recv_handler_send, o, BReactor_PendingGroup(device->reactor)); + + DebugCounter_Increment(&peer->d_receivers_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPReceiveReceiver_Free (DPReceiveReceiver *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->peer->d_receivers_ctr); + + // free receive interface + PacketPassInterface_Free(&o->recv_if); +} + +PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->recv_if; +} diff --git a/app/src/main/jni/badvpn/client/DPReceive.h b/app/src/main/jni/badvpn/client/DPReceive.h new file mode 100644 index 0000000..ecc5a05 --- /dev/null +++ b/app/src/main/jni/badvpn/client/DPReceive.h @@ -0,0 +1,98 @@ +/** + * @file DPReceive.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Receive processing for the VPN client. + */ + +#ifndef BADVPN_CLIENT_DPRECEIVE_H +#define BADVPN_CLIENT_DPRECEIVE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*DPReceiveDevice_output_func) (void *output_user, uint8_t *data, int data_len); + +struct DPReceiveReceiver_s; + +typedef struct { + int device_mtu; + DPReceiveDevice_output_func output_func; + void *output_func_user; + BReactor *reactor; + int relay_flow_buffer_size; + int relay_flow_inactivity_time; + int packet_mtu; + DPRelayRouter relay_router; + int have_peer_id; + peerid_t peer_id; + LinkedList1 peers_list; + DebugObject d_obj; +} DPReceiveDevice; + +typedef struct { + DPReceiveDevice *device; + peerid_t peer_id; + FrameDeciderPeer *decider_peer; + int is_relay_client; + DPRelaySource relay_source; + DPRelaySink relay_sink; + DataProtoSink *dp_sink; + LinkedList1Node list_node; + DebugObject d_obj; + DebugCounter d_receivers_ctr; +} DPReceivePeer; + +typedef struct DPReceiveReceiver_s { + DPReceivePeer *peer; + DPReceiveDevice *device; + PacketPassInterface recv_if; + DebugObject d_obj; +} DPReceiveReceiver; + +int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time) WARN_UNUSED; +void DPReceiveDevice_Free (DPReceiveDevice *o); +void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id); + +void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client); +void DPReceivePeer_Free (DPReceivePeer *o); +void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink); +void DPReceivePeer_DetachSink (DPReceivePeer *o); + +void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer); +void DPReceiveReceiver_Free (DPReceiveReceiver *o); +PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/DPRelay.c b/app/src/main/jni/badvpn/client/DPRelay.c new file mode 100644 index 0000000..983e3ad --- /dev/null +++ b/app/src/main/jni/badvpn/client/DPRelay.c @@ -0,0 +1,307 @@ +/** + * @file DPRelay.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +#include + +static void flow_inactivity_handler (struct DPRelay_flow *flow); + +static struct DPRelay_flow * create_flow (DPRelaySource *src, DPRelaySink *sink, int num_packets, int inactivity_time) +{ + ASSERT(num_packets > 0) + + // allocate structure + struct DPRelay_flow *flow = (struct DPRelay_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "relay flow %d->%d: malloc failed", (int)src->source_id, (int)sink->dest_id); + goto fail0; + } + + // set src and sink + flow->src = src; + flow->sink = sink; + + // init DataProtoFlow + if (!DataProtoFlow_Init(&flow->dp_flow, &src->router->dp_source, src->source_id, sink->dest_id, num_packets, inactivity_time, flow, (DataProtoFlow_handler_inactivity)flow_inactivity_handler)) { + BLog(BLOG_ERROR, "relay flow %d->%d: DataProtoFlow_Init failed", (int)src->source_id, (int)sink->dest_id); + goto fail1; + } + + // insert to source list + LinkedList1_Append(&src->flows_list, &flow->src_list_node); + + // insert to sink list + LinkedList1_Append(&sink->flows_list, &flow->sink_list_node); + + // attach flow if needed + if (sink->dp_sink) { + DataProtoFlow_Attach(&flow->dp_flow, sink->dp_sink); + } + + BLog(BLOG_INFO, "relay flow %d->%d: created", (int)src->source_id, (int)sink->dest_id); + + return flow; + +fail1: + free(flow); +fail0: + return NULL; +} + +static void free_flow (struct DPRelay_flow *flow) +{ + // detach flow if needed + if (flow->sink->dp_sink) { + DataProtoFlow_Detach(&flow->dp_flow); + } + + // remove posible router reference + if (flow->src->router->current_flow == flow) { + flow->src->router->current_flow = NULL; + } + + // remove from sink list + LinkedList1_Remove(&flow->sink->flows_list, &flow->sink_list_node); + + // remove from source list + LinkedList1_Remove(&flow->src->flows_list, &flow->src_list_node); + + // free DataProtoFlow + DataProtoFlow_Free(&flow->dp_flow); + + // free structore + free(flow); +} + +static void flow_inactivity_handler (struct DPRelay_flow *flow) +{ + BLog(BLOG_INFO, "relay flow %d->%d: timed out", (int)flow->src->source_id, (int)flow->sink->dest_id); + + free_flow(flow); +} + +static struct DPRelay_flow * source_find_flow (DPRelaySource *o, DPRelaySink *sink) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node); + ASSERT(flow->src == o) + if (flow->sink == sink) { + return flow; + } + } + + return NULL; +} + +static void router_dp_source_handler (DPRelayRouter *o, const uint8_t *frame, int frame_len) +{ + DebugObject_Access(&o->d_obj); + + if (!o->current_flow) { + return; + } + + // route frame to current flow + DataProtoFlow_Route(&o->current_flow->dp_flow, 0); + + // set no current flow + o->current_flow = NULL; +} + +int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor) +{ + ASSERT(frame_mtu >= 0) + ASSERT(frame_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + + // init arguments + o->frame_mtu = frame_mtu; + + // init BufferWriter + BufferWriter_Init(&o->writer, frame_mtu, BReactor_PendingGroup(reactor)); + + // init DataProtoSource + if (!DataProtoSource_Init(&o->dp_source, BufferWriter_GetOutput(&o->writer), (DataProtoSource_handler)router_dp_source_handler, o, reactor)) { + BLog(BLOG_ERROR, "DataProtoSource_Init failed"); + goto fail1; + } + + // have no current flow + o->current_flow = NULL; + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + BufferWriter_Free(&o->writer); + return 0; +} + +void DPRelayRouter_Free (DPRelayRouter *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + ASSERT(!o->current_flow) // have no sources + + // free DataProtoSource + DataProtoSource_Free(&o->dp_source); + + // free BufferWriter + BufferWriter_Free(&o->writer); +} + +void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time) +{ + DebugObject_Access(&o->d_obj); + DebugObject_Access(&src->d_obj); + DebugObject_Access(&sink->d_obj); + ASSERT(!o->current_flow) + ASSERT(src->router == o) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->frame_mtu) + ASSERT(num_packets > 0) + + // get memory location + uint8_t *out; + if (!BufferWriter_StartPacket(&o->writer, &out)) { + BLog(BLOG_ERROR, "BufferWriter_StartPacket failed for frame %d->%d !?", (int)src->source_id, (int)sink->dest_id); + return; + } + + // write frame + memcpy(out, data, data_len); + + // submit frame + BufferWriter_EndPacket(&o->writer, data_len); + + // get a flow + // this comes _after_ writing the packet, in case flow initialization schedules jobs + struct DPRelay_flow *flow = source_find_flow(src, sink); + if (!flow) { + if (!(flow = create_flow(src, sink, num_packets, inactivity_time))) { + return; + } + } + + // remember flow so we know where to route the frame in router_dp_source_handler + o->current_flow = flow; +} + +void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor) +{ + DebugObject_Access(&router->d_obj); + + // init arguments + o->router = router; + o->source_id = source_id; + + // init flows list + LinkedList1_Init(&o->flows_list); + + DebugCounter_Increment(&o->router->d_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPRelaySource_Free (DPRelaySource *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->router->d_ctr); + + // free flows, detaching them if needed + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&o->flows_list)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node); + free_flow(flow); + } +} + +void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id) +{ + // init arguments + o->dest_id = dest_id; + + // init flows list + LinkedList1_Init(&o->flows_list); + + // have no sink + o->dp_sink = NULL; + + DebugObject_Init(&o->d_obj); +} + +void DPRelaySink_Free (DPRelaySink *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(!o->dp_sink) + + // free flows + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&o->flows_list)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + free_flow(flow); + } +} + +void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->dp_sink) + ASSERT(dp_sink) + + // attach flows + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + DataProtoFlow_Attach(&flow->dp_flow, dp_sink); + } + + // set sink + o->dp_sink = dp_sink; +} + +void DPRelaySink_Detach (DPRelaySink *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->dp_sink) + + // detach flows + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + DataProtoFlow_Detach(&flow->dp_flow); + } + + // set no sink + o->dp_sink = NULL; +} diff --git a/app/src/main/jni/badvpn/client/DPRelay.h b/app/src/main/jni/badvpn/client/DPRelay.h new file mode 100644 index 0000000..c5e16eb --- /dev/null +++ b/app/src/main/jni/badvpn/client/DPRelay.h @@ -0,0 +1,89 @@ +/** + * @file DPRelay.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CLIENT_DPRELAY_H +#define BADVPN_CLIENT_DPRELAY_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct DPRelay_flow; + +typedef struct { + int frame_mtu; + BufferWriter writer; + DataProtoSource dp_source; + struct DPRelay_flow *current_flow; + DebugObject d_obj; + DebugCounter d_ctr; +} DPRelayRouter; + +typedef struct { + DPRelayRouter *router; + peerid_t source_id; + LinkedList1 flows_list; + DebugObject d_obj; +} DPRelaySource; + +typedef struct { + peerid_t dest_id; + LinkedList1 flows_list; + DataProtoSink *dp_sink; + DebugObject d_obj; +} DPRelaySink; + +struct DPRelay_flow { + DPRelaySource *src; + DPRelaySink *sink; + DataProtoFlow dp_flow; + LinkedList1Node src_list_node; + LinkedList1Node sink_list_node; +}; + +int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor) WARN_UNUSED; +void DPRelayRouter_Free (DPRelayRouter *o); +void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time); + +void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor); +void DPRelaySource_Free (DPRelaySource *o); + +void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id); +void DPRelaySink_Free (DPRelaySink *o); +void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink); +void DPRelaySink_Detach (DPRelaySink *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/DataProto.c b/app/src/main/jni/badvpn/client/DataProto.c new file mode 100644 index 0000000..255045a --- /dev/null +++ b/app/src/main/jni/badvpn/client/DataProto.c @@ -0,0 +1,566 @@ +/** + * @file DataProto.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +static void monitor_handler (DataProtoSink *o); +static void refresh_up_job (DataProtoSink *o); +static void receive_timer_handler (DataProtoSink *o); +static void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len); +static void up_job_handler (DataProtoSink *o); +static void flow_buffer_free (struct DataProtoFlow_buffer *b); +static void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink); +static void flow_buffer_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b); + +void monitor_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + + // send keep-alive + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); +} + +void refresh_up_job (DataProtoSink *o) +{ + if (o->up != o->up_report) { + BPending_Set(&o->up_job); + } else { + BPending_Unset(&o->up_job); + } +} + +void receive_timer_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + + // consider down + o->up = 0; + + refresh_up_job(o); +} + +void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= sizeof(struct dataproto_header)) + + int flags = 0; + + // if we are receiving keepalives, set the flag + if (BTimer_IsRunning(&o->receive_timer)) { + flags |= DATAPROTO_FLAGS_RECEIVING_KEEPALIVES; + } + + // modify existing packet here + struct dataproto_header header; + memcpy(&header, data, sizeof(header)); + header.flags = hton8(flags); + memcpy(data, &header, sizeof(header)); +} + +void up_job_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up != o->up_report) + + o->up_report = o->up; + + o->handler(o->user, o->up); + return; +} + +void source_router_handler (DataProtoSource *o, uint8_t *buf, int recv_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(buf) + ASSERT(recv_len >= 0) + ASSERT(recv_len <= o->frame_mtu) + + // remember packet + o->current_buf = buf; + o->current_recv_len = recv_len; + + // call handler + o->handler(o->user, buf + DATAPROTO_MAX_OVERHEAD, recv_len); + return; +} + +void flow_buffer_free (struct DataProtoFlow_buffer *b) +{ + ASSERT(!b->sink) + + // free route buffer + RouteBuffer_Free(&b->rbuf); + + // free inactivity monitor + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Free(&b->monitor); + } + + // free connector + PacketPassConnector_Free(&b->connector); + + // free buffer structure + free(b); +} + +void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink) +{ + ASSERT(!b->sink) + + // init queue flow + PacketPassFairQueueFlow_Init(&b->sink_qflow, &sink->queue); + + // connect to queue flow + PacketPassConnector_ConnectOutput(&b->connector, PacketPassFairQueueFlow_GetInput(&b->sink_qflow)); + + // set DataProto + b->sink = sink; +} + +void flow_buffer_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + // disconnect from queue flow + PacketPassConnector_DisconnectOutput(&b->connector); + + // free queue flow + PacketPassFairQueueFlow_Free(&b->sink_qflow); + + // clear reference to this buffer in the sink + if (b->sink->detaching_buffer == b) { + b->sink->detaching_buffer = NULL; + } + + // set no DataProto + b->sink = NULL; +} + +void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) + ASSERT(!b->sink->detaching_buffer || b->sink->detaching_buffer == b) + + if (b->sink->detaching_buffer == b) { + return; + } + + // request cancel + PacketPassFairQueueFlow_RequestCancel(&b->sink_qflow); + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&b->sink_qflow, (PacketPassFairQueue_handler_busy)flow_buffer_qflow_handler_busy, b); + + // remember this buffer in the sink so it can handle us if it goes away + b->sink->detaching_buffer = b; +} + +void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(b->sink->detaching_buffer == b) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + // detach + flow_buffer_detach(b); + + if (!b->flow) { + // free + flow_buffer_free(b); + } else if (b->flow->sink_desired) { + // attach + flow_buffer_attach(b, b->flow->sink_desired); + } +} + +void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(b->sink->detaching_buffer == b) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + flow_buffer_finish_detach(b); +} + +int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user) +{ + ASSERT(PacketPassInterface_HasCancel(output)) + ASSERT(PacketPassInterface_GetMTU(output) >= DATAPROTO_MAX_OVERHEAD) + + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // set frame MTU + o->frame_mtu = PacketPassInterface_GetMTU(output) - DATAPROTO_MAX_OVERHEAD; + + // init notifier + PacketPassNotifier_Init(&o->notifier, output, BReactor_PendingGroup(o->reactor)); + PacketPassNotifier_SetHandler(&o->notifier, (PacketPassNotifier_handler_notify)notifier_handler, o); + + // init monitor + PacketPassInactivityMonitor_Init(&o->monitor, PacketPassNotifier_GetInput(&o->notifier), o->reactor, keepalive_time, (PacketPassInactivityMonitor_handler)monitor_handler, o); + PacketPassInactivityMonitor_Force(&o->monitor); + + // init queue + if (!PacketPassFairQueue_Init(&o->queue, PacketPassInactivityMonitor_GetInput(&o->monitor), BReactor_PendingGroup(o->reactor), 1, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail1; + } + + // init keepalive queue flow + PacketPassFairQueueFlow_Init(&o->ka_qflow, &o->queue); + + // init keepalive source + DataProtoKeepaliveSource_Init(&o->ka_source, BReactor_PendingGroup(o->reactor)); + + // init keepalive blocker + PacketRecvBlocker_Init(&o->ka_blocker, DataProtoKeepaliveSource_GetOutput(&o->ka_source), BReactor_PendingGroup(o->reactor)); + + // init keepalive buffer + if (!SinglePacketBuffer_Init(&o->ka_buffer, PacketRecvBlocker_GetOutput(&o->ka_blocker), PacketPassFairQueueFlow_GetInput(&o->ka_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init receive timer + BTimer_Init(&o->receive_timer, tolerance_time, (BTimer_handler)receive_timer_handler, o); + + // init handler job + BPending_Init(&o->up_job, BReactor_PendingGroup(o->reactor), (BPending_handler)up_job_handler, o); + + // set not up + o->up = 0; + o->up_report = 0; + + // set no detaching buffer + o->detaching_buffer = NULL; + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + PacketRecvBlocker_Free(&o->ka_blocker); + DataProtoKeepaliveSource_Free(&o->ka_source); + PacketPassFairQueueFlow_Free(&o->ka_qflow); + PacketPassFairQueue_Free(&o->queue); +fail1: + PacketPassInactivityMonitor_Free(&o->monitor); + PacketPassNotifier_Free(&o->notifier); + return 0; +} + +void DataProtoSink_Free (DataProtoSink *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + // allow freeing queue flows + PacketPassFairQueue_PrepareFree(&o->queue); + + // release detaching buffer + if (o->detaching_buffer) { + ASSERT(!o->detaching_buffer->flow || o->detaching_buffer->flow->sink_desired != o) + flow_buffer_finish_detach(o->detaching_buffer); + } + + // free handler job + BPending_Free(&o->up_job); + + // free receive timer + BReactor_RemoveTimer(o->reactor, &o->receive_timer); + + // free keepalive buffer + SinglePacketBuffer_Free(&o->ka_buffer); + + // free keepalive blocker + PacketRecvBlocker_Free(&o->ka_blocker); + + // free keepalive source + DataProtoKeepaliveSource_Free(&o->ka_source); + + // free keepalive queue flow + PacketPassFairQueueFlow_Free(&o->ka_qflow); + + // free queue + PacketPassFairQueue_Free(&o->queue); + + // free monitor + PacketPassInactivityMonitor_Free(&o->monitor); + + // free notifier + PacketPassNotifier_Free(&o->notifier); +} + +void DataProtoSink_Received (DataProtoSink *o, int peer_receiving) +{ + ASSERT(peer_receiving == 0 || peer_receiving == 1) + DebugObject_Access(&o->d_obj); + + // reset receive timer + BReactor_SetTimer(o->reactor, &o->receive_timer); + + if (!peer_receiving) { + // peer reports not receiving, consider down + o->up = 0; + // send keep-alive to converge faster + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); + } else { + // consider up + o->up = 1; + } + + refresh_up_job(o); +} + +int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + ASSERT(handler) + + // init arguments + o->handler = handler; + o->user = user; + o->reactor = reactor; + + // remember frame MTU + o->frame_mtu = PacketRecvInterface_GetMTU(input); + + // init router + if (!PacketRouter_Init(&o->router, DATAPROTO_MAX_OVERHEAD + o->frame_mtu, DATAPROTO_MAX_OVERHEAD, input, (PacketRouter_handler)source_router_handler, o, BReactor_PendingGroup(reactor))) { + BLog(BLOG_ERROR, "PacketRouter_Init failed"); + goto fail0; + } + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void DataProtoSource_Free (DataProtoSource *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + // free router + PacketRouter_Free(&o->router); +} + +int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user, + DataProtoFlow_handler_inactivity handler_inactivity) +{ + DebugObject_Access(&source->d_obj); + ASSERT(num_packets > 0) + ASSERT(!(inactivity_time >= 0) || handler_inactivity) + + // init arguments + o->source = source; + o->source_id = source_id; + o->dest_id = dest_id; + + // set no desired sink + o->sink_desired = NULL; + + // allocate buffer structure + struct DataProtoFlow_buffer *b = (struct DataProtoFlow_buffer *)malloc(sizeof(*b)); + if (!b) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + o->b = b; + + // set parent + b->flow = o; + + // remember inactivity time + b->inactivity_time = inactivity_time; + + // init connector + PacketPassConnector_Init(&b->connector, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, BReactor_PendingGroup(source->reactor)); + + // init inactivity monitor + PacketPassInterface *buf_out = PacketPassConnector_GetInput(&b->connector); + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Init(&b->monitor, buf_out, source->reactor, b->inactivity_time, handler_inactivity, user); + buf_out = PacketPassInactivityMonitor_GetInput(&b->monitor); + } + + // init route buffer + if (!RouteBuffer_Init(&b->rbuf, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, buf_out, num_packets)) { + BLog(BLOG_ERROR, "RouteBuffer_Init failed"); + goto fail1; + } + + // set no sink + b->sink = NULL; + + DebugCounter_Increment(&source->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Free(&b->monitor); + } + PacketPassConnector_Free(&b->connector); + free(b); +fail0: + return 0; +} + +void DataProtoFlow_Free (DataProtoFlow *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->source->d_ctr); + ASSERT(!o->sink_desired) + struct DataProtoFlow_buffer *b = o->b; + + if (b->sink) { + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach, free buffer after detach + flow_buffer_schedule_detach(b); + b->flow = NULL; + + // remove inactivity handler + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_SetHandler(&b->monitor, NULL, NULL); + } + } else { + // detach and free buffer now + flow_buffer_detach(b); + flow_buffer_free(b); + } + } else { + // free buffer + flow_buffer_free(b); + } +} + +void DataProtoFlow_Route (DataProtoFlow *o, int more) +{ + DebugObject_Access(&o->d_obj); + PacketRouter_AssertRoute(&o->source->router); + ASSERT(o->source->current_buf) + ASSERT(more == 0 || more == 1) + struct DataProtoFlow_buffer *b = o->b; + + // write header. Don't set flags, it will be set in notifier_handler. + struct dataproto_header header; + struct dataproto_peer_id id; + header.from_id = htol16(o->source_id); + header.num_peer_ids = htol16(1); + id.id = htol16(o->dest_id); + memcpy(o->source->current_buf, &header, sizeof(header)); + memcpy(o->source->current_buf + sizeof(header), &id, sizeof(id)); + + // route + uint8_t *next_buf; + if (!PacketRouter_Route(&o->source->router, DATAPROTO_MAX_OVERHEAD + o->source->current_recv_len, &b->rbuf, + &next_buf, DATAPROTO_MAX_OVERHEAD, (more ? o->source->current_recv_len : 0) + )) { + BLog(BLOG_NOTICE, "buffer full: %d->%d", (int)o->source_id, (int)o->dest_id); + return; + } + + // remember next buffer, or don't allow further routing if more==0 + o->source->current_buf = (more ? next_buf : NULL); +} + +void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink) +{ + DebugObject_Access(&o->d_obj); + DebugObject_Access(&sink->d_obj); + ASSERT(!o->sink_desired) + ASSERT(sink) + ASSERT(o->source->frame_mtu <= sink->frame_mtu) + struct DataProtoFlow_buffer *b = o->b; + + if (b->sink) { + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach and reattach + flow_buffer_schedule_detach(b); + } else { + // detach and reattach now + flow_buffer_detach(b); + flow_buffer_attach(b, sink); + } + } else { + // attach + flow_buffer_attach(b, sink); + } + + // set desired sink + o->sink_desired = sink; + + DebugCounter_Increment(&sink->d_ctr); +} + +void DataProtoFlow_Detach (DataProtoFlow *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sink_desired) + struct DataProtoFlow_buffer *b = o->b; + ASSERT(b->sink) + + DataProtoSink *sink = o->sink_desired; + + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach + flow_buffer_schedule_detach(b); + } else { + // detach now + flow_buffer_detach(b); + } + + // set no desired sink + o->sink_desired = NULL; + + DebugCounter_Decrement(&sink->d_ctr); +} diff --git a/app/src/main/jni/badvpn/client/DataProto.h b/app/src/main/jni/badvpn/client/DataProto.h new file mode 100644 index 0000000..0da3a20 --- /dev/null +++ b/app/src/main/jni/badvpn/client/DataProto.h @@ -0,0 +1,237 @@ +/** + * @file DataProto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mudule for frame sending used in the VPN client program. + */ + +#ifndef BADVPN_CLIENT_DATAPROTO_H +#define BADVPN_CLIENT_DATAPROTO_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*DataProtoSink_handler) (void *user, int up); +typedef void (*DataProtoSource_handler) (void *user, const uint8_t *frame, int frame_len); +typedef void (*DataProtoFlow_handler_inactivity) (void *user); + +struct DataProtoFlow_buffer; + +/** + * Frame destination. + * Represents a peer as a destination for sending frames to. + */ +typedef struct { + BReactor *reactor; + int frame_mtu; + PacketPassFairQueue queue; + PacketPassInactivityMonitor monitor; + PacketPassNotifier notifier; + DataProtoKeepaliveSource ka_source; + PacketRecvBlocker ka_blocker; + SinglePacketBuffer ka_buffer; + PacketPassFairQueueFlow ka_qflow; + BTimer receive_timer; + int up; + int up_report; + DataProtoSink_handler handler; + void *user; + BPending up_job; + struct DataProtoFlow_buffer *detaching_buffer; + DebugObject d_obj; + DebugCounter d_ctr; +} DataProtoSink; + +/** + * Receives frames from a {@link PacketRecvInterface} input and + * allows the user to route them to buffers in {@link DataProtoFlow}'s. + */ +typedef struct { + DataProtoSource_handler handler; + void *user; + BReactor *reactor; + int frame_mtu; + PacketRouter router; + uint8_t *current_buf; + int current_recv_len; + DebugObject d_obj; + DebugCounter d_ctr; +} DataProtoSource; + +/** + * Contains a buffer for frames from a specific peer to a specific peer. + * Receives frames from a {@link DataProtoSource} as routed by the user. + * Can be attached to a {@link DataProtoSink} to send out frames. + */ +typedef struct { + DataProtoSource *source; + peerid_t source_id; + peerid_t dest_id; + DataProtoSink *sink_desired; + struct DataProtoFlow_buffer *b; + DebugObject d_obj; +} DataProtoFlow; + +struct DataProtoFlow_buffer { + DataProtoFlow *flow; + int inactivity_time; + RouteBuffer rbuf; + PacketPassInactivityMonitor monitor; + PacketPassConnector connector; + DataProtoSink *sink; + PacketPassFairQueueFlow sink_qflow; +}; + +/** + * Initializes the sink. + * + * @param o the object + * @param reactor reactor we live in + * @param output output interface. Must support cancel functionality. Its MTU must be + * >=DATAPROTO_MAX_OVERHEAD. + * @param keepalive_time keepalive time + * @param tolerance_time after how long of not having received anything from the peer + * to consider the link down + * @param handler up state handler + * @param user value to pass to handler + * @return 1 on success, 0 on failure + */ +int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the sink. + * There must be no local sources attached. + * + * @param o the object + */ +void DataProtoSink_Free (DataProtoSink *o); + +/** + * Notifies the sink that a packet was received from the peer. + * Must not be in freeing state. + * + * @param o the object + * @param peer_receiving whether the DATAPROTO_FLAGS_RECEIVING_KEEPALIVES flag was set in the packet. + * Must be 0 or 1. + */ +void DataProtoSink_Received (DataProtoSink *o, int peer_receiving); + +/** + * Initiazes the source. + * + * @param o the object + * @param input frame input. Its input MTU must be <= INT_MAX - DATAPROTO_MAX_OVERHEAD. + * @param handler handler called when a frame arrives to allow the user to route it to + * appropriate {@link DataProtoFlow}'s. + * @param user value passed to handler + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the source. + * There must be no {@link DataProtoFlow}'s using this source. + * + * @param o the object + */ +void DataProtoSource_Free (DataProtoSource *o); + +/** + * Initializes the flow. + * The flow is initialized in not attached state. + * + * @param o the object + * @param source source to receive frames from + * @param source_id source peer ID to encode in the headers (i.e. our ID) + * @param dest_id destination peer ID to encode in the headers (i.e. ID if the peer this + * flow belongs to) + * @param num_packets number of packets the buffer should hold. Must be >0. + * @param inactivity_time milliseconds of output inactivity after which to call the + * inactivity handler; <0 to disable. Note that the flow is considered + * active as long as its buffer is non-empty, even if is not attached to + * a {@link DataProtoSink}. + * @param user value to pass to handler + * @param handler_inactivity inactivity handler, if inactivity_time >=0 + * @return 1 on success, 0 on failure + */ +int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user, + DataProtoFlow_handler_inactivity handler_inactivity) WARN_UNUSED; + +/** + * Frees the flow. + * The flow must be in not attached state. + * + * @param o the object + */ +void DataProtoFlow_Free (DataProtoFlow *o); + +/** + * Routes a frame from the flow's source to this flow. + * Must be called from within the job context of the {@link DataProtoSource_handler} handler. + * Must not be called after this has been called with more=0 for the current frame. + * + * @param o the object + * @param more whether the current frame may have to be routed to more + * flows. If 0, must not be called again until the handler is + * called for the next frame. Must be 0 or 1. + */ +void DataProtoFlow_Route (DataProtoFlow *o, int more); + +/** + * Attaches the flow to a sink. + * The flow must be in not attached state. + * + * @param o the object + * @param sink sink to attach to. This flow's frame_mtu must be <= + * (output MTU of sink) - DATAPROTO_MAX_OVERHEAD. + */ +void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink); + +/** + * Detaches the flow from a destination. + * The flow must be in attached state. + * + * @param o the object + */ +void DataProtoFlow_Detach (DataProtoFlow *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.c b/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.c new file mode 100644 index 0000000..834c42f --- /dev/null +++ b/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.c @@ -0,0 +1,72 @@ +/** + * @file DataProtoKeepaliveSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "DataProtoKeepaliveSource.h" + +static void output_handler_recv (DataProtoKeepaliveSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + struct dataproto_header header; + header.flags = htol8(0); + header.from_id = htol16(0); + header.num_peer_ids = htol16(0); + memcpy(data, &header, sizeof(header)); + + // finish packet + PacketRecvInterface_Done(&o->output, sizeof(struct dataproto_header)); +} + +void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg) +{ + // init output + PacketRecvInterface_Init(&o->output, sizeof(struct dataproto_header), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.h b/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.h new file mode 100644 index 0000000..4488e24 --- /dev/null +++ b/app/src/main/jni/badvpn/client/DataProtoKeepaliveSource.h @@ -0,0 +1,73 @@ +/** + * @file DataProtoKeepaliveSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} source which provides DataProto keepalive packets. + */ + +#ifndef BADVPN_DATAPROTOKEEPALIVESOURCE_H +#define BADVPN_DATAPROTOKEEPALIVESOURCE_H + +#include +#include + +/** + * A {@link PacketRecvInterface} source which provides DataProto keepalive packets. + * These packets have no payload, no destination peers and flags zero. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface output; +} DataProtoKeepaliveSource; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be sizeof(struct dataproto_header). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/DatagramPeerIO.c b/app/src/main/jni/badvpn/client/DatagramPeerIO.c new file mode 100644 index 0000000..e3a8f68 --- /dev/null +++ b/app/src/main/jni/badvpn/client/DatagramPeerIO.c @@ -0,0 +1,425 @@ +/** + * @file DatagramPeerIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#define DATAGRAMPEERIO_MODE_NONE 0 +#define DATAGRAMPEERIO_MODE_CONNECT 1 +#define DATAGRAMPEERIO_MODE_BIND 2 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void init_io (DatagramPeerIO *o); +static void free_io (DatagramPeerIO *o); +static void dgram_handler (DatagramPeerIO *o, int event); +static void reset_mode (DatagramPeerIO *o); +static void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len); + +void init_io (DatagramPeerIO *o) +{ + // init dgram recv interface + BDatagram_RecvAsync_Init(&o->dgram, o->effective_socket_mtu); + + // connect source + PacketRecvConnector_ConnectInput(&o->recv_connector, BDatagram_RecvAsync_GetIf(&o->dgram)); + + // init dgram send interface + BDatagram_SendAsync_Init(&o->dgram, o->effective_socket_mtu); + + // connect sink + PacketPassConnector_ConnectOutput(&o->send_connector, BDatagram_SendAsync_GetIf(&o->dgram)); +} + +void free_io (DatagramPeerIO *o) +{ + // disconnect sink + PacketPassConnector_DisconnectOutput(&o->send_connector); + + // free dgram send interface + BDatagram_SendAsync_Free(&o->dgram); + + // disconnect source + PacketRecvConnector_DisconnectInput(&o->recv_connector); + + // free dgram recv interface + BDatagram_RecvAsync_Free(&o->dgram); +} + +void dgram_handler (DatagramPeerIO *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) + + PeerLog(o, BLOG_NOTICE, "error"); + + // reset mode + reset_mode(o); + + // report error + if (o->handler_error) { + o->handler_error(o->user); + return; + } +} + +void reset_mode (DatagramPeerIO *o) +{ + ASSERT(o->mode == DATAGRAMPEERIO_MODE_NONE || o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) + + if (o->mode == DATAGRAMPEERIO_MODE_NONE) { + return; + } + + // remove recv notifier handler + PacketPassNotifier_SetHandler(&o->recv_notifier, NULL, NULL); + + // free I/O + free_io(o); + + // free datagram object + BDatagram_Free(&o->dgram); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_NONE; +} + +void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len) +{ + ASSERT(o->mode == DATAGRAMPEERIO_MODE_BIND) + DebugObject_Access(&o->d_obj); + + // obtain addresses from last received packet + BAddr addr; + BIPAddr local_addr; + ASSERT_EXECUTE(BDatagram_GetLastReceiveAddrs(&o->dgram, &addr, &local_addr)) + + // check address family just in case + if (!BDatagram_AddressFamilySupported(addr.type)) { + PeerLog(o, BLOG_ERROR, "unsupported receive address"); + return; + } + + // update addresses + BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); +} + +int DatagramPeerIO_Init ( + DatagramPeerIO *o, + BReactor *reactor, + int payload_mtu, + int socket_mtu, + struct spproto_security_params sp_params, + btime_t latency, + int num_frames, + PacketPassInterface *recv_userif, + int otp_warning_count, + BThreadWorkDispatcher *twd, + void *user, + BLog_logfunc logfunc, + DatagramPeerIO_handler_error handler_error, + DatagramPeerIO_handler_otp_warning handler_otp_warning, + DatagramPeerIO_handler_otp_ready handler_otp_ready +) +{ + ASSERT(payload_mtu >= 0) + ASSERT(socket_mtu >= 0) + spproto_assert_security_params(sp_params); + ASSERT(num_frames > 0) + ASSERT(PacketPassInterface_GetMTU(recv_userif) >= payload_mtu) + if (SPPROTO_HAVE_OTP(sp_params)) { + ASSERT(otp_warning_count > 0) + ASSERT(otp_warning_count <= sp_params.otp_num) + } + + // set parameters + o->reactor = reactor; + o->payload_mtu = payload_mtu; + o->sp_params = sp_params; + o->user = user; + o->logfunc = logfunc; + o->handler_error = handler_error; + + // check num frames (for FragmentProtoAssembler) + if (num_frames >= FPA_MAX_TIME) { + PeerLog(o, BLOG_ERROR, "num_frames is too big"); + goto fail0; + } + + // check payload MTU (for FragmentProto) + if (o->payload_mtu > UINT16_MAX) { + PeerLog(o, BLOG_ERROR, "payload MTU is too big"); + goto fail0; + } + + // calculate SPProto payload MTU + if ((o->spproto_payload_mtu = spproto_payload_mtu_for_carrier_mtu(o->sp_params, socket_mtu)) <= (int)sizeof(struct fragmentproto_chunk_header)) { + PeerLog(o, BLOG_ERROR, "socket MTU is too small"); + goto fail0; + } + + // calculate effective socket MTU + if ((o->effective_socket_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->spproto_payload_mtu)) < 0) { + PeerLog(o, BLOG_ERROR, "spproto_carrier_mtu_for_payload_mtu failed !?"); + goto fail0; + } + + // init receiving + + // init assembler + if (!FragmentProtoAssembler_Init(&o->recv_assembler, o->spproto_payload_mtu, recv_userif, num_frames, fragmentproto_max_chunks_for_frame(o->spproto_payload_mtu, o->payload_mtu), + BReactor_PendingGroup(o->reactor), o->user, o->logfunc + )) { + PeerLog(o, BLOG_ERROR, "FragmentProtoAssembler_Init failed"); + goto fail0; + } + + // init notifier + PacketPassNotifier_Init(&o->recv_notifier, FragmentProtoAssembler_GetInput(&o->recv_assembler), BReactor_PendingGroup(o->reactor)); + + // init decoder + if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor), twd, o->user, o->logfunc)) { + PeerLog(o, BLOG_ERROR, "SPProtoDecoder_Init failed"); + goto fail1; + } + SPProtoDecoder_SetHandlers(&o->recv_decoder, handler_otp_ready, user); + + // init connector + PacketRecvConnector_Init(&o->recv_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->recv_buffer, PacketRecvConnector_GetOutput(&o->recv_connector), SPProtoDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init sending base + + // init disassembler + FragmentProtoDisassembler_Init(&o->send_disassembler, o->reactor, o->payload_mtu, o->spproto_payload_mtu, -1, latency); + + // init encoder + if (!SPProtoEncoder_Init(&o->send_encoder, FragmentProtoDisassembler_GetOutput(&o->send_disassembler), o->sp_params, otp_warning_count, BReactor_PendingGroup(o->reactor), twd)) { + PeerLog(o, BLOG_ERROR, "SPProtoEncoder_Init failed"); + goto fail3; + } + SPProtoEncoder_SetHandlers(&o->send_encoder, handler_otp_warning, user); + + // init connector + PacketPassConnector_Init(&o->send_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->send_buffer, SPProtoEncoder_GetOutput(&o->send_encoder), PacketPassConnector_GetInput(&o->send_connector), BReactor_PendingGroup(o->reactor))) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + // set mode + o->mode = DATAGRAMPEERIO_MODE_NONE; + + DebugObject_Init(&o->d_obj); + return 1; + +fail4: + PacketPassConnector_Free(&o->send_connector); + SPProtoEncoder_Free(&o->send_encoder); +fail3: + FragmentProtoDisassembler_Free(&o->send_disassembler); + SinglePacketBuffer_Free(&o->recv_buffer); +fail2: + PacketRecvConnector_Free(&o->recv_connector); + SPProtoDecoder_Free(&o->recv_decoder); +fail1: + PacketPassNotifier_Free(&o->recv_notifier); + FragmentProtoAssembler_Free(&o->recv_assembler); +fail0: + return 0; +} + +void DatagramPeerIO_Free (DatagramPeerIO *o) +{ + DebugObject_Free(&o->d_obj); + + // reset mode + reset_mode(o); + + // free sending base + SinglePacketBuffer_Free(&o->send_buffer); + PacketPassConnector_Free(&o->send_connector); + SPProtoEncoder_Free(&o->send_encoder); + FragmentProtoDisassembler_Free(&o->send_disassembler); + + // free receiving + SinglePacketBuffer_Free(&o->recv_buffer); + PacketRecvConnector_Free(&o->recv_connector); + SPProtoDecoder_Free(&o->recv_decoder); + PacketPassNotifier_Free(&o->recv_notifier); + FragmentProtoAssembler_Free(&o->recv_assembler); +} + +PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o) +{ + DebugObject_Access(&o->d_obj); + + return FragmentProtoDisassembler_GetInput(&o->send_disassembler); +} + +int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + + // reset mode + reset_mode(o); + + // check address + if (!BDatagram_AddressFamilySupported(addr.type)) { + PeerLog(o, BLOG_ERROR, "BDatagram_AddressFamilySupported failed"); + goto fail0; + } + + // init dgram + if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { + PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // set send address + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); + + // init I/O + init_io(o); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_CONNECT; + + return 1; + +fail0: + return 0; +} + +int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // reset mode + reset_mode(o); + + // init dgram + if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { + PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // bind dgram + if (!BDatagram_Bind(&o->dgram, addr)) { + PeerLog(o, BLOG_INFO, "BDatagram_Bind failed"); + goto fail1; + } + + // init I/O + init_io(o); + + // set recv notifier handler + PacketPassNotifier_SetHandler(&o->recv_notifier, (PacketPassNotifier_handler_notify)recv_decoder_notifier_handler, o); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_BIND; + + return 1; + +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // set sending key + SPProtoEncoder_SetEncryptionKey(&o->send_encoder, encryption_key); + + // set receiving key + SPProtoDecoder_SetEncryptionKey(&o->recv_decoder, encryption_key); +} + +void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove sending key + SPProtoEncoder_RemoveEncryptionKey(&o->send_encoder); + + // remove receiving key + SPProtoDecoder_RemoveEncryptionKey(&o->recv_decoder); +} + +void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // set sending seed + SPProtoEncoder_SetOTPSeed(&o->send_encoder, seed_id, key, iv); +} + +void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove sending seed + SPProtoEncoder_RemoveOTPSeed(&o->send_encoder); +} + +void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // add receiving seed + SPProtoDecoder_AddOTPSeed(&o->recv_decoder, seed_id, key, iv); +} + +void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove receiving seeds + SPProtoDecoder_RemoveOTPSeeds(&o->recv_decoder); +} diff --git a/app/src/main/jni/badvpn/client/DatagramPeerIO.h b/app/src/main/jni/badvpn/client/DatagramPeerIO.h new file mode 100644 index 0000000..5d19b5a --- /dev/null +++ b/app/src/main/jni/badvpn/client/DatagramPeerIO.h @@ -0,0 +1,271 @@ +/** + * @file DatagramPeerIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for comminicating with a peer using a datagram socket. + */ + +#ifndef BADVPN_CLIENT_DATAGRAMPEERIO_H +#define BADVPN_CLIENT_DATAGRAMPEERIO_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Callback function invoked when an error occurs with the peer connection. + * The object has entered default state. + * May be called from within a sending Send call. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_error) (void *user); + +/** + * Handler function invoked when the number of used OTPs has reached + * the specified warning number in {@link DatagramPeerIO_SetOTPWarningHandler}. + * May be called from within a sending Send call. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_otp_warning) (void *user); + +/** + * Handler called when OTP generation for a new receive seed is finished. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_otp_ready) (void *user); + +/** + * Object for comminicating with a peer using a datagram socket. + * + * The user provides data for sending to the peer through {@link PacketPassInterface}. + * Received data is provided to the user through {@link PacketPassInterface}. + * + * The object has a logical state called a mode, which is one of the following: + * - default - nothing is send or received + * - connecting - an address was provided by the user for sending datagrams to. + * Datagrams are being sent to that address through a socket, + * and datagrams are being received on the same socket. + * - binding - an address was provided by the user to bind a socket to. + * Datagrams are being received on the socket. Datagrams are not being + * sent initially. When a datagram is received, its source address is + * used as a destination address for sending datagrams. + */ +typedef struct { + DebugObject d_obj; + BReactor *reactor; + int payload_mtu; + struct spproto_security_params sp_params; + void *user; + BLog_logfunc logfunc; + DatagramPeerIO_handler_error handler_error; + int spproto_payload_mtu; + int effective_socket_mtu; + + // sending base + FragmentProtoDisassembler send_disassembler; + SPProtoEncoder send_encoder; + SinglePacketBuffer send_buffer; + PacketPassConnector send_connector; + + // receiving + PacketRecvConnector recv_connector; + SinglePacketBuffer recv_buffer; + SPProtoDecoder recv_decoder; + PacketPassNotifier recv_notifier; + FragmentProtoAssembler recv_assembler; + + // mode + int mode; + + // datagram object + BDatagram dgram; +} DatagramPeerIO; + +/** + * Initializes the object. + * The interface is initialized in default mode. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param reactor {@link BReactor} we live in + * @param payload_mtu maximum payload size. Must be >=0. + * @param socket_mtu maximum datagram size for the socket. Must be >=0. Must be large enough so it is possible to + * send a FragmentProto chunk with one byte of data over SPProto, i.e. the following has to hold: + * spproto_payload_mtu_for_carrier_mtu(sp_params, socket_mtu) > sizeof(struct fragmentproto_chunk_header) + * @param sp_params SPProto security parameters + * @param latency latency parameter to {@link FragmentProtoDisassembler_Init}. + * @param num_frames num_frames parameter to {@link FragmentProtoAssembler_Init}. Must be >0. + * @param recv_userif interface to pass received packets to the user. Its MTU must be >=payload_mtu. + * @param otp_warning_count If using OTPs, after how many encoded packets to call the handler. + * In this case, must be >0 and <=sp_params.otp_num. + * @param twd thread work dispatcher + * @param user value to pass to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @param handler_error error handler + * @param handler_otp_warning OTP warning handler + * @param handler_otp_ready handler called when OTP generation for a new receive seed is finished + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Init ( + DatagramPeerIO *o, + BReactor *reactor, + int payload_mtu, + int socket_mtu, + struct spproto_security_params sp_params, + btime_t latency, + int num_frames, + PacketPassInterface *recv_userif, + int otp_warning_count, + BThreadWorkDispatcher *twd, + void *user, + BLog_logfunc logfunc, + DatagramPeerIO_handler_error handler_error, + DatagramPeerIO_handler_otp_warning handler_otp_warning, + DatagramPeerIO_handler_otp_ready handler_otp_ready +) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void DatagramPeerIO_Free (DatagramPeerIO *o); + +/** + * Returns an interface the user should use to send packets. + * The OTP warning handler may be called from within Send calls + * to the interface. + * + * @param o the object + * @return sending interface + */ +PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o); + +/** + * Attempts to establish connection to the peer which has bound to an address. + * On success, the interface enters connecting mode. + * On failure, the interface enters default mode. + * + * @param o the object + * @param addr address to send packets to + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) WARN_UNUSED; + +/** + * Attempts to establish connection to the peer by binding to an address. + * On success, the interface enters connecting mode. + * On failure, the interface enters default mode. + * + * @param o the object + * @param addr address to bind to. Must be supported according to + * {@link BDatagram_AddressFamilySupported}. + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) WARN_UNUSED; + +/** + * Sets the encryption key to use for sending and receiving. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key); + +/** + * Removed the encryption key to use for sending and receiving. + * Encryption must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o); + +/** + * Sets the OTP seed for sending. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes the OTP seed for sending of one is configured. + * OTPs must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o); + +/** + * Adds an OTP seed for reciving. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all OTP seeds for reciving. + * OTPs must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/FragmentProtoAssembler.c b/app/src/main/jni/badvpn/client/FragmentProtoAssembler.c new file mode 100644 index 0000000..8588c2e --- /dev/null +++ b/app/src/main/jni/badvpn/client/FragmentProtoAssembler.c @@ -0,0 +1,469 @@ +/** + * @file FragmentProtoAssembler.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "FragmentProtoAssembler.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#include "FragmentProtoAssembler_tree.h" +#include + +static void free_frame (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame) +{ + // remove from used list + LinkedList1_Remove(&o->frames_used, &frame->list_node); + // remove from used tree + FPAFramesTree_Remove(&o->frames_used_tree, 0, frame); + + // append to free list + LinkedList1_Append(&o->frames_free, &frame->list_node); +} + +static void free_oldest_frame (FragmentProtoAssembler *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->frames_used)) + + // obtain oldest frame (first on the list) + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used); + ASSERT(list_node) + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + + // free frame + free_frame(o, frame); +} + +static struct FragmentProtoAssembler_frame * allocate_new_frame (FragmentProtoAssembler *o, fragmentproto_frameid id) +{ + ASSERT(!FPAFramesTree_LookupExact(&o->frames_used_tree, 0, id)) + + // if there are no free entries, free the oldest used one + if (LinkedList1_IsEmpty(&o->frames_free)) { + PeerLog(o, BLOG_INFO, "freeing used frame"); + free_oldest_frame(o); + } + + // obtain frame entry + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_free); + ASSERT(list_node) + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + + // remove from free list + LinkedList1_Remove(&o->frames_free, &frame->list_node); + + // initialize values + frame->id = id; + frame->time = o->time; + frame->num_chunks = 0; + frame->sum = 0; + frame->length = -1; + frame->length_so_far = 0; + + // append to used list + LinkedList1_Append(&o->frames_used, &frame->list_node); + // insert to used tree + int res = FPAFramesTree_Insert(&o->frames_used_tree, 0, frame, NULL); + ASSERT_EXECUTE(res) + + return frame; +} + +static int chunks_overlap (int c1_start, int c1_len, int c2_start, int c2_len) +{ + return (c1_start + c1_len > c2_start && c2_start + c2_len > c1_start); +} + +static int frame_is_timed_out (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame) +{ + ASSERT(frame->time <= o->time) + + return (o->time - frame->time > o->time_tolerance); +} + +static void reduce_times (FragmentProtoAssembler *o) +{ + // find the frame with minimal time, removing timed out frames + struct FragmentProtoAssembler_frame *minframe = NULL; + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used); + while (list_node) { + LinkedList1Node *next = LinkedList1Node_Next(list_node); + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + if (frame_is_timed_out(o, frame)) { + PeerLog(o, BLOG_INFO, "freeing timed out frame (while reducing times)"); + free_frame(o, frame); + } else { + if (!minframe || frame->time < minframe->time) { + minframe = frame; + } + } + list_node = next; + } + + if (!minframe) { + // have no frames, set packet time to zero + o->time = 0; + return; + } + + uint32_t min_time = minframe->time; + + // subtract minimal time from all frames + for (list_node = LinkedList1_GetFirst(&o->frames_used); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + frame->time -= min_time; + } + + // subtract minimal time from packet time + o->time -= min_time; +} + +static int process_chunk (FragmentProtoAssembler *o, fragmentproto_frameid frame_id, int chunk_start, int chunk_len, int is_last, uint8_t *payload) +{ + ASSERT(chunk_start >= 0) + ASSERT(chunk_len >= 0) + ASSERT(is_last == 0 || is_last == 1) + + // verify chunk + + // check start + if (chunk_start > o->output_mtu) { + PeerLog(o, BLOG_INFO, "chunk starts outside"); + return 0; + } + + // check frame size bound + if (chunk_len > o->output_mtu - chunk_start) { + PeerLog(o, BLOG_INFO, "chunk ends outside"); + return 0; + } + + // calculate end + int chunk_end = chunk_start + chunk_len; + ASSERT(chunk_end >= 0) + ASSERT(chunk_end <= o->output_mtu) + + // lookup frame + struct FragmentProtoAssembler_frame *frame = FPAFramesTree_LookupExact(&o->frames_used_tree, 0, frame_id); + if (!frame) { + // frame not found, add a new one + frame = allocate_new_frame(o, frame_id); + } else { + // have existing frame with that ID + // check frame time + if (frame_is_timed_out(o, frame)) { + // frame is timed out, remove it and use a new one + PeerLog(o, BLOG_INFO, "freeing timed out frame (while processing chunk)"); + free_frame(o, frame); + frame = allocate_new_frame(o, frame_id); + } + } + + ASSERT(frame->num_chunks < o->num_chunks) + + // check if the chunk overlaps with any existing chunks + for (int i = 0; i < frame->num_chunks; i++) { + struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[i]; + if (chunks_overlap(chunk->start, chunk->len, chunk_start, chunk_len)) { + PeerLog(o, BLOG_INFO, "chunk overlaps with existing chunk"); + goto fail_frame; + } + } + + if (is_last) { + // this chunk is marked as last + if (frame->length >= 0) { + PeerLog(o, BLOG_INFO, "got last chunk, but already have one"); + goto fail_frame; + } + // check if frame size according to this packet is consistent + // with existing chunks + if (frame->length_so_far > chunk_end) { + PeerLog(o, BLOG_INFO, "got last chunk, but already have data over its bound"); + goto fail_frame; + } + } else { + // if we have length, chunk must be in its bound + if (frame->length >= 0) { + if (chunk_end > frame->length) { + PeerLog(o, BLOG_INFO, "chunk out of length bound"); + goto fail_frame; + } + } + } + + // chunk is good, add it + + // update frame time + frame->time = o->time; + + // add chunk entry + struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[frame->num_chunks]; + chunk->start = chunk_start; + chunk->len = chunk_len; + frame->num_chunks++; + + // update sum + frame->sum += chunk_len; + + // update length + if (is_last) { + frame->length = chunk_end; + } else { + if (frame->length < 0) { + if (frame->length_so_far < chunk_end) { + frame->length_so_far = chunk_end; + } + } + } + + // copy chunk payload to buffer + memcpy(frame->buffer + chunk_start, payload, chunk_len); + + // is frame incomplete? + if (frame->length < 0 || frame->sum < frame->length) { + // if all chunks are used, fail it + if (frame->num_chunks == o->num_chunks) { + PeerLog(o, BLOG_INFO, "all chunks used, but frame not complete"); + goto fail_frame; + } + + // wait for more chunks + return 0; + } + + ASSERT(frame->sum == frame->length) + + PeerLog(o, BLOG_DEBUG, "frame complete"); + + // free frame entry + free_frame(o, frame); + + // send frame + PacketPassInterface_Sender_Send(o->output, frame->buffer, frame->length); + + return 1; + +fail_frame: + free_frame(o, frame); + return 0; +} + +static void process_input (FragmentProtoAssembler *o) +{ + ASSERT(o->in_len >= 0) + + // read chunks + while (o->in_pos < o->in_len) { + // obtain header + if (o->in_len - o->in_pos < sizeof(struct fragmentproto_chunk_header)) { + PeerLog(o, BLOG_INFO, "too little data for chunk header"); + break; + } + struct fragmentproto_chunk_header header; + memcpy(&header, o->in + o->in_pos, sizeof(header)); + o->in_pos += sizeof(struct fragmentproto_chunk_header); + fragmentproto_frameid frame_id = ltoh16(header.frame_id); + int chunk_start = ltoh16(header.chunk_start); + int chunk_len = ltoh16(header.chunk_len); + int is_last = ltoh8(header.is_last); + + // check is_last field + if (!(is_last == 0 || is_last == 1)) { + PeerLog(o, BLOG_INFO, "chunk is_last wrong"); + break; + } + + // obtain data + if (o->in_len - o->in_pos < chunk_len) { + PeerLog(o, BLOG_INFO, "too little data for chunk data"); + break; + } + + // process chunk + int res = process_chunk(o, frame_id, chunk_start, chunk_len, is_last, o->in + o->in_pos); + o->in_pos += chunk_len; + + if (res) { + // sending complete frame, stop processing input + return; + } + } + + // increment packet time + if (o->time == FPA_MAX_TIME) { + reduce_times(o); + if (!LinkedList1_IsEmpty(&o->frames_used)) { + ASSERT(o->time < FPA_MAX_TIME) // If there was a frame with zero time, it was removed because + // time_tolerance < FPA_MAX_TIME. So something >0 was subtracted. + o->time++; + } else { + // it was set to zero by reduce_times + ASSERT(o->time == 0) + } + } else { + o->time++; + } + + // set no input packet + o->in_len = -1; + + // finish input + PacketPassInterface_Done(&o->input); +} + +static void input_handler_send (FragmentProtoAssembler *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // save input packet + o->in_len = data_len; + o->in = data; + o->in_pos = 0; + + process_input(o); +} + +static void output_handler_done (FragmentProtoAssembler *o) +{ + ASSERT(o->in_len >= 0) + DebugObject_Access(&o->d_obj); + + process_input(o); +} + +int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc) +{ + ASSERT(input_mtu >= 0) + ASSERT(num_frames > 0) + ASSERT(num_frames < FPA_MAX_TIME) // needed so we can always subtract times when packet time is maximum + ASSERT(num_chunks > 0) + + // init arguments + o->output = output; + o->num_chunks = num_chunks; + o->user = user; + o->logfunc = logfunc; + + // init input + PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // remebmer output MTU + o->output_mtu = PacketPassInterface_GetMTU(o->output); + + // set packet time to zero + o->time = 0; + + // set time tolerance to num_frames + o->time_tolerance = num_frames; + + // allocate frames + if (!(o->frames_entries = (struct FragmentProtoAssembler_frame *)BAllocArray(num_frames, sizeof(o->frames_entries[0])))) { + goto fail1; + } + + // allocate chunks + if (!(o->frames_chunks = (struct FragmentProtoAssembler_chunk *)BAllocArray2(num_frames, o->num_chunks, sizeof(o->frames_chunks[0])))) { + goto fail2; + } + + // allocate buffers + if (!(o->frames_buffer = (uint8_t *)BAllocArray(num_frames, o->output_mtu))) { + goto fail3; + } + + // init frame lists + LinkedList1_Init(&o->frames_free); + LinkedList1_Init(&o->frames_used); + + // initialize frame entries + for (int i = 0; i < num_frames; i++) { + struct FragmentProtoAssembler_frame *frame = &o->frames_entries[i]; + // set chunks array pointer + frame->chunks = o->frames_chunks + (size_t)i * o->num_chunks; + // set buffer pointer + frame->buffer = o->frames_buffer + (size_t)i * o->output_mtu; + // add to free list + LinkedList1_Append(&o->frames_free, &frame->list_node); + } + + // init tree + FPAFramesTree_Init(&o->frames_used_tree); + + // have no input packet + o->in_len = -1; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail3: + BFree(o->frames_chunks); +fail2: + BFree(o->frames_entries); +fail1: + PacketPassInterface_Free(&o->input); + return 0; +} + +void FragmentProtoAssembler_Free (FragmentProtoAssembler *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffers + BFree(o->frames_buffer); + + // free chunks + BFree(o->frames_chunks); + + // free frames + BFree(o->frames_entries); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/app/src/main/jni/badvpn/client/FragmentProtoAssembler.h b/app/src/main/jni/badvpn/client/FragmentProtoAssembler.h new file mode 100644 index 0000000..bbc5483 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FragmentProtoAssembler.h @@ -0,0 +1,134 @@ +/** + * @file FragmentProtoAssembler.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes packets according to FragmentProto. + */ + +#ifndef BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H +#define BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPA_MAX_TIME UINT32_MAX + +struct FragmentProtoAssembler_frame; + +#include "FragmentProtoAssembler_tree.h" +#include + +struct FragmentProtoAssembler_chunk { + int start; + int len; +}; + +struct FragmentProtoAssembler_frame { + LinkedList1Node list_node; // node in free or used list + struct FragmentProtoAssembler_chunk *chunks; // array of chunks, up to num_chunks + uint8_t *buffer; // buffer with frame data, size output_mtu + // everything below only defined when frame entry is used + fragmentproto_frameid id; // frame identifier + uint32_t time; // packet time when the last chunk was received + FPAFramesTreeNode tree_node; // node fields in tree for searching frames by id + int num_chunks; // number of valid chunks + int sum; // sum of all chunks' lengths + int length; // length of the frame, or -1 if not yet known + int length_so_far; // if length=-1, current data set's upper bound +}; + +/** + * Object which decodes packets according to FragmentProto. + * + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + void *user; + BLog_logfunc logfunc; + PacketPassInterface input; + PacketPassInterface *output; + int output_mtu; + int num_chunks; + uint32_t time; + int time_tolerance; + struct FragmentProtoAssembler_frame *frames_entries; + struct FragmentProtoAssembler_chunk *frames_chunks; + uint8_t *frames_buffer; + LinkedList1 frames_free; + LinkedList1 frames_used; + FPAFramesTree frames_used_tree; + int in_len; + uint8_t *in; + int in_pos; + DebugObject d_obj; +} FragmentProtoAssembler; + +/** + * Initializes the object. + * {@link BLog_Init} must have been done. + * + * @param o the object + * @param input_mtu maximum input packet size. Must be >=0. + * @param output output interface + * @param num_frames number of frames we can hold. Must be >0 and < FPA_MAX_TIME. + * To make the assembler tolerate out-of-order input of degree D, set to D+2. + * Here, D is the minimum size of a hypothetical buffer needed to order the input. + * @param num_chunks maximum number of chunks a frame can come in. Must be >0. + * @param pg pending group + * @param user argument to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void FragmentProtoAssembler_Free (FragmentProtoAssembler *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/FragmentProtoAssembler_tree.h b/app/src/main/jni/badvpn/client/FragmentProtoAssembler_tree.h new file mode 100644 index 0000000..744c633 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FragmentProtoAssembler_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FPAFramesTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct FragmentProtoAssembler_frame +#define SAVL_PARAM_TYPE_KEY fragmentproto_frameid +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->id, (entry2)->id) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->id) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.c b/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.c new file mode 100644 index 0000000..e67a1dc --- /dev/null +++ b/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.c @@ -0,0 +1,229 @@ +/** + * @file FragmentProtoDisassembler.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "client/FragmentProtoDisassembler.h" + +static void write_chunks (FragmentProtoDisassembler *o) +{ + #define IN_AVAIL (o->in_len - o->in_used) + #define OUT_AVAIL ((o->output_mtu - o->out_used) - (int)sizeof(struct fragmentproto_chunk_header)) + + ASSERT(o->in_len >= 0) + ASSERT(o->out) + ASSERT(OUT_AVAIL > 0) + + // write chunks to output packet + do { + // calculate chunk length + int chunk_len = bmin_int(IN_AVAIL, OUT_AVAIL); + if (o->chunk_mtu > 0) { + chunk_len = bmin_int(chunk_len, o->chunk_mtu); + } + + // write chunk header + struct fragmentproto_chunk_header header; + header.frame_id = htol16(o->frame_id); + header.chunk_start = htol16(o->in_used); + header.chunk_len = htol16(chunk_len); + header.is_last = (chunk_len == IN_AVAIL); + memcpy(o->out + o->out_used, &header, sizeof(header)); + + // write chunk data + memcpy(o->out + o->out_used + sizeof(struct fragmentproto_chunk_header), o->in + o->in_used, chunk_len); + + // increment pointers + o->in_used += chunk_len; + o->out_used += sizeof(struct fragmentproto_chunk_header) + chunk_len; + } while (IN_AVAIL > 0 && OUT_AVAIL > 0); + + // have we finished the input packet? + if (IN_AVAIL == 0) { + // set no input packet + o->in_len = -1; + + // increment frame ID + o->frame_id++; + + // finish input + PacketPassInterface_Done(&o->input); + } + + // should we finish the output packet? + if (OUT_AVAIL <= 0 || o->latency < 0) { + // set no output packet + o->out = NULL; + + // stop timer (if it's running) + if (o->latency >= 0) { + BReactor_RemoveTimer(o->reactor, &o->timer); + } + + // finish output + PacketRecvInterface_Done(&o->output, o->out_used); + } else { + // start timer if we have output and it's not running (output was empty before) + if (!BTimer_IsRunning(&o->timer)) { + BReactor_SetTimer(o->reactor, &o->timer); + } + } +} + +static void input_handler_send (FragmentProtoDisassembler *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(o->in_len == -1) + + // set input packet + o->in_len = data_len; + o->in = data; + o->in_used = 0; + + // if there is no output, wait for it + if (!o->out) { + return; + } + + write_chunks(o); +} + +static void input_handler_requestcancel (FragmentProtoDisassembler *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->out) + + // set no input packet + o->in_len = -1; + + // finish input + PacketPassInterface_Done(&o->input); +} + +static void output_handler_recv (FragmentProtoDisassembler *o, uint8_t *data) +{ + ASSERT(data) + ASSERT(!o->out) + + // set output packet + o->out = data; + o->out_used = 0; + + // if there is no input, wait for it + if (o->in_len < 0) { + return; + } + + write_chunks(o); +} + +static void timer_handler (FragmentProtoDisassembler *o) +{ + ASSERT(o->latency >= 0) + ASSERT(o->out) + ASSERT(o->in_len == -1) + + // set no output packet + o->out = NULL; + + // finish output + PacketRecvInterface_Done(&o->output, o->out_used); +} + +void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency) +{ + ASSERT(input_mtu >= 0) + ASSERT(input_mtu <= UINT16_MAX) + ASSERT(output_mtu > sizeof(struct fragmentproto_chunk_header)) + ASSERT(chunk_mtu > 0 || chunk_mtu < 0) + + // init arguments + o->reactor = reactor; + o->output_mtu = output_mtu; + o->chunk_mtu = chunk_mtu; + o->latency = latency; + + // init input + PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(reactor)); + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(reactor)); + + // init timer + if (o->latency >= 0) { + BTimer_Init(&o->timer, o->latency, (BTimer_handler)timer_handler, o); + } + + // have no input packet + o->in_len = -1; + + // have no output packet + o->out = NULL; + + // start with zero frame ID + o->frame_id = 0; + + DebugObject_Init(&o->d_obj); +} + +void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o) +{ + DebugObject_Free(&o->d_obj); + + // free timer + if (o->latency >= 0) { + BReactor_RemoveTimer(o->reactor, &o->timer); + } + + // free output + PacketRecvInterface_Free(&o->output); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.h b/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.h new file mode 100644 index 0000000..49fe9c8 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FragmentProtoDisassembler.h @@ -0,0 +1,109 @@ +/** + * @file FragmentProtoDisassembler.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets into packets composed of chunks + * according to FragmentProto. + */ + +#ifndef BADVPN_CLIENT_CCPROTODISASSEMBLER_H +#define BADVPN_CLIENT_CCPROTODISASSEMBLER_H + +#include + +#include +#include +#include +#include +#include +#include + +/** + * Object which encodes packets into packets composed of chunks + * according to FragmentProto. + * + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + BReactor *reactor; + int output_mtu; + int chunk_mtu; + btime_t latency; + PacketPassInterface input; + PacketRecvInterface output; + BTimer timer; + int in_len; + uint8_t *in; + int in_used; + uint8_t *out; + int out_used; + fragmentproto_frameid frame_id; + DebugObject d_obj; +} FragmentProtoDisassembler; + +/** + * Initializes the object. + * + * @param o the object + * @param reactor reactor we live in + * @param input_mtu maximum input packet size. Must be >=0 and <=UINT16_MAX. + * @param output_mtu maximum output packet size. Must be >sizeof(struct fragmentproto_chunk_header). + * @param chunk_mtu maximum chunk size. Must be >0, or <0 for no explicit limit. + * @param latency maximum time a pending output packet with some data can wait for more data + * before being sent out. If nonnegative, a timer will be used. If negative, + * packets will always be sent out immediately. If low latency is desired, + * prefer setting this to zero rather than negative. + */ +void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency); + +/** + * Frees the object. + * + * @param o the object + */ +void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/FrameDecider.c b/app/src/main/jni/badvpn/client/FrameDecider.c new file mode 100644 index 0000000..e7bb4de --- /dev/null +++ b/app/src/main/jni/badvpn/client/FrameDecider.c @@ -0,0 +1,795 @@ +/** + * @file FrameDecider.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DECIDE_STATE_NONE 1 +#define DECIDE_STATE_UNICAST 2 +#define DECIDE_STATE_FLOOD 3 +#define DECIDE_STATE_MULTICAST 4 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static int compare_macs (const uint8_t *mac1, const uint8_t *mac2) +{ + int c = memcmp(mac1, mac2, 6); + return B_COMPARE(c, 0); +} + +#include "FrameDecider_macs_tree.h" +#include + +#include "FrameDecider_groups_tree.h" +#include + +#include "FrameDecider_multicast_tree.h" +#include + +static void add_mac_to_peer (FrameDeciderPeer *o, uint8_t *mac) +{ + FrameDecider *d = o->d; + + // locate entry in tree + struct _FrameDecider_mac_entry *e_entry = FDMacsTree_LookupExact(&d->macs_tree, 0, mac); + if (e_entry) { + if (e_entry->peer == o) { + // this is our MAC; only move it to the end of the used list + LinkedList1_Remove(&o->mac_entries_used, &e_entry->list_node); + LinkedList1_Append(&o->mac_entries_used, &e_entry->list_node); + return; + } + + // some other peer has that MAC; disassociate it + FDMacsTree_Remove(&d->macs_tree, 0, e_entry); + LinkedList1_Remove(&e_entry->peer->mac_entries_used, &e_entry->list_node); + LinkedList1_Append(&e_entry->peer->mac_entries_free, &e_entry->list_node); + } + + // aquire MAC address entry, if there are no free ones reuse the oldest used one + LinkedList1Node *list_node; + struct _FrameDecider_mac_entry *entry; + if (list_node = LinkedList1_GetFirst(&o->mac_entries_free)) { + entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node); + ASSERT(entry->peer == o) + + // remove from free + LinkedList1_Remove(&o->mac_entries_free, &entry->list_node); + } else { + list_node = LinkedList1_GetFirst(&o->mac_entries_used); + ASSERT(list_node) + entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node); + ASSERT(entry->peer == o) + + // remove from used + FDMacsTree_Remove(&d->macs_tree, 0, entry); + LinkedList1_Remove(&o->mac_entries_used, &entry->list_node); + } + + PeerLog(o, BLOG_INFO, "adding MAC %02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8"", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // set MAC in entry + memcpy(entry->mac, mac, sizeof(entry->mac)); + + // add to used + LinkedList1_Append(&o->mac_entries_used, &entry->list_node); + int res = FDMacsTree_Insert(&d->macs_tree, 0, entry, NULL); + ASSERT_EXECUTE(res) +} + +static uint32_t compute_sig_for_group (uint32_t group) +{ + return hton32(ntoh32(group)&0x7FFFFF); +} + +static uint32_t compute_sig_for_mac (uint8_t *mac) +{ + uint32_t sig; + memcpy(&sig, mac + 2, 4); + sig = hton32(ntoh32(sig)&0x7FFFFF); + return sig; +} + +static void add_to_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry) +{ + // compute sig + uint32_t sig = compute_sig_for_group(group_entry->group); + + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig); + if (master) { + // use existing master + ASSERT(master->is_master) + + // set not master + group_entry->is_master = 0; + + // insert to list + LinkedList3Node_InitAfter(&group_entry->sig_list_node, &master->sig_list_node); + } else { + // make this entry master + + // set master + group_entry->is_master = 1; + + // set sig + group_entry->master.sig = sig; + + // insert to multicast tree + int res = FDMulticastTree_Insert(&d->multicast_tree, 0, group_entry, NULL); + ASSERT_EXECUTE(res) + + // init list node + LinkedList3Node_InitLonely(&group_entry->sig_list_node); + } +} + +static void remove_from_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry) +{ + // compute sig + uint32_t sig = compute_sig_for_group(group_entry->group); + + if (group_entry->is_master) { + // remove master from multicast tree + FDMulticastTree_Remove(&d->multicast_tree, 0, group_entry); + + if (!LinkedList3Node_IsLonely(&group_entry->sig_list_node)) { + // at least one more group entry for this sig; make another entry the master + + // get an entry + LinkedList3Node *list_node = LinkedList3Node_NextOrPrev(&group_entry->sig_list_node); + struct _FrameDecider_group_entry *newmaster = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node); + ASSERT(!newmaster->is_master) + + // set master + newmaster->is_master = 1; + + // set sig + newmaster->master.sig = sig; + + // insert to multicast tree + int res = FDMulticastTree_Insert(&d->multicast_tree, 0, newmaster, NULL); + ASSERT_EXECUTE(res) + } + } + + // free linked list node + LinkedList3Node_Free(&group_entry->sig_list_node); +} + +static void add_group_to_peer (FrameDeciderPeer *o, uint32_t group) +{ + FrameDecider *d = o->d; + + struct _FrameDecider_group_entry *group_entry = FDGroupsTree_LookupExact(&o->groups_tree, 0, group); + if (group_entry) { + // move to end of used list + LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node); + LinkedList1_Append(&o->group_entries_used, &group_entry->list_node); + } else { + PeerLog(o, BLOG_INFO, "joined group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", + ((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3] + ); + + // aquire group entry, if there are no free ones reuse the earliest used one + LinkedList1Node *node; + if (node = LinkedList1_GetFirst(&o->group_entries_free)) { + group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from free list + LinkedList1_Remove(&o->group_entries_free, &group_entry->list_node); + } else { + node = LinkedList1_GetFirst(&o->group_entries_used); + ASSERT(node) + group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from multicast + remove_from_multicast(d, group_entry); + + // remove from peer's groups tree + FDGroupsTree_Remove(&o->groups_tree, 0, group_entry); + + // remove from used list + LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node); + } + + // add entry to used list + LinkedList1_Append(&o->group_entries_used, &group_entry->list_node); + + // set group address + group_entry->group = group; + + // insert to peer's groups tree + int res = FDGroupsTree_Insert(&o->groups_tree, 0, group_entry, NULL); + ASSERT_EXECUTE(res) + + // add to multicast + add_to_multicast(d, group_entry); + } + + // set timer + group_entry->timer_endtime = btime_gettime() + d->igmp_group_membership_interval; + BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime); +} + +static void remove_group_entry (struct _FrameDecider_group_entry *group_entry) +{ + FrameDeciderPeer *peer = group_entry->peer; + FrameDecider *d = peer->d; + + uint32_t group = group_entry->group; + + PeerLog(peer, BLOG_INFO, "left group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", + ((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3] + ); + + // remove from multicast + remove_from_multicast(d, group_entry); + + // remove from peer's groups tree + FDGroupsTree_Remove(&peer->groups_tree, 0, group_entry); + + // remove from used list + LinkedList1_Remove(&peer->group_entries_used, &group_entry->list_node); + + // add to free list + LinkedList1_Append(&peer->group_entries_free, &group_entry->list_node); + + // stop timer + BReactor_RemoveTimer(d->reactor, &group_entry->timer); +} + +static void lower_group_timers_to_lmqt (FrameDecider *d, uint32_t group) +{ + // have to lower all the group timers of this group down to LMQT + + // compute sig + uint32_t sig = compute_sig_for_group(group); + + // look up the sig in multicast tree + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig); + if (!master) { + return; + } + ASSERT(master->is_master) + + // iterate all group entries with this sig + LinkedList3Iterator it; + LinkedList3Iterator_Init(&it, LinkedList3Node_First(&master->sig_list_node), 1); + LinkedList3Node *sig_list_node; + while (sig_list_node = LinkedList3Iterator_Next(&it)) { + struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(sig_list_node, struct _FrameDecider_group_entry, sig_list_node); + + // skip wrong groups + if (group_entry->group != group) { + continue; + } + + // lower timer down to LMQT + btime_t now = btime_gettime(); + if (group_entry->timer_endtime > now + d->igmp_last_member_query_time) { + group_entry->timer_endtime = now + d->igmp_last_member_query_time; + BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime); + } + } +} + +static void group_entry_timer_handler (struct _FrameDecider_group_entry *group_entry) +{ + DebugObject_Access(&group_entry->peer->d_obj); + + remove_group_entry(group_entry); +} + +void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor) +{ + ASSERT(max_peer_macs > 0) + ASSERT(max_peer_groups > 0) + + // init arguments + o->max_peer_macs = max_peer_macs; + o->max_peer_groups = max_peer_groups; + o->igmp_group_membership_interval = igmp_group_membership_interval; + o->igmp_last_member_query_time = igmp_last_member_query_time; + o->reactor = reactor; + + // init peers list + LinkedList1_Init(&o->peers_list); + + // init MAC tree + FDMacsTree_Init(&o->macs_tree); + + // init multicast tree + FDMulticastTree_Init(&o->multicast_tree); + + // init decide state + o->decide_state = DECIDE_STATE_NONE; + + // set no current flood peer + o->decide_flood_current = NULL; + + DebugObject_Init(&o->d_obj); +} + +void FrameDecider_Free (FrameDecider *o) +{ + ASSERT(FDMulticastTree_IsEmpty(&o->multicast_tree)) + ASSERT(FDMacsTree_IsEmpty(&o->macs_tree)) + ASSERT(LinkedList1_IsEmpty(&o->peers_list)) + DebugObject_Free(&o->d_obj); +} + +void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + DebugObject_Access(&o->d_obj); + + // reset decide state + switch (o->decide_state) { + case DECIDE_STATE_NONE: + break; + case DECIDE_STATE_UNICAST: + break; + case DECIDE_STATE_FLOOD: + break; + case DECIDE_STATE_MULTICAST: + LinkedList3Iterator_Free(&o->decide_multicast_it); + return; + default: + ASSERT(0); + } + o->decide_state = DECIDE_STATE_NONE; + o->decide_flood_current = NULL; + + // analyze frame + + const uint8_t *pos = frame; + int len = frame_len; + + if (len < sizeof(struct ethernet_header)) { + return; + } + struct ethernet_header eh; + memcpy(&eh, pos, sizeof(eh)); + pos += sizeof(struct ethernet_header); + len -= sizeof(struct ethernet_header); + + int is_igmp = 0; + + switch (ntoh16(eh.type)) { + case ETHERTYPE_IPV4: { + // check IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) { + BLog(BLOG_INFO, "decide: wrong IP packet"); + goto out; + } + + // check if it's IGMP + if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) { + goto out; + } + + // remember that it's IGMP; we have to flood IGMP frames + is_igmp = 1; + + // check IGMP header + if (len < sizeof(struct igmp_base)) { + BLog(BLOG_INFO, "decide: IGMP: short packet"); + goto out; + } + struct igmp_base igmp_base; + memcpy(&igmp_base, pos, sizeof(igmp_base)); + pos += sizeof(struct igmp_base); + len -= sizeof(struct igmp_base); + + switch (ntoh8(igmp_base.type)) { + case IGMP_TYPE_MEMBERSHIP_QUERY: { + if (len == sizeof(struct igmp_v2_extra) && ntoh8(igmp_base.max_resp_code) != 0) { + // V2 query + struct igmp_v2_extra query; + memcpy(&query, pos, sizeof(query)); + pos += sizeof(struct igmp_v2_extra); + len -= sizeof(struct igmp_v2_extra); + + if (ntoh32(query.group) != 0) { + // got a Group-Specific Query, lower group timers to LMQT + lower_group_timers_to_lmqt(o, query.group); + } + } + else if (len >= sizeof(struct igmp_v3_query_extra)) { + // V3 query + struct igmp_v3_query_extra query; + memcpy(&query, pos, sizeof(query)); + pos += sizeof(struct igmp_v3_query_extra); + len -= sizeof(struct igmp_v3_query_extra); + + // iterate sources + uint16_t num_sources = ntoh16(query.number_of_sources); + int i; + for (i = 0; i < num_sources; i++) { + // check source + if (len < sizeof(struct igmp_source)) { + BLog(BLOG_NOTICE, "decide: IGMP: short source"); + goto out; + } + pos += sizeof(struct igmp_source); + len -= sizeof(struct igmp_source); + } + + if (ntoh32(query.group) != 0 && num_sources == 0) { + // got a Group-Specific Query, lower group timers to LMQT + lower_group_timers_to_lmqt(o, query.group); + } + } + } break; + } + } break; + } + +out:; + + const uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const uint8_t multicast_mac_header[] = {0x01, 0x00, 0x5e}; + + // if it's broadcast or IGMP, flood it + if (is_igmp || !memcmp(eh.dest, broadcast_mac, sizeof(broadcast_mac))) { + o->decide_state = DECIDE_STATE_FLOOD; + o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list); + return; + } + + // if it's multicast, forward to all peers with the given sig + if (!memcmp(eh.dest, multicast_mac_header, sizeof(multicast_mac_header))) { + // extract group's sig from destination MAC + uint32_t sig = compute_sig_for_mac(eh.dest); + + // look up the sig in multicast tree + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&o->multicast_tree, 0, sig); + if (master) { + ASSERT(master->is_master) + + o->decide_state = DECIDE_STATE_MULTICAST; + LinkedList3Iterator_Init(&o->decide_multicast_it, LinkedList3Node_First(&master->sig_list_node), 1); + } + + return; + } + + // look for MAC entry + struct _FrameDecider_mac_entry *entry = FDMacsTree_LookupExact(&o->macs_tree, 0, eh.dest); + if (entry) { + o->decide_state = DECIDE_STATE_UNICAST; + o->decide_unicast_peer = entry->peer; + return; + } + + // unknown destination MAC, flood + o->decide_state = DECIDE_STATE_FLOOD; + o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list); + return; +} + +FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o) +{ + DebugObject_Access(&o->d_obj); + + switch (o->decide_state) { + case DECIDE_STATE_NONE: { + return NULL; + } break; + + case DECIDE_STATE_UNICAST: { + o->decide_state = DECIDE_STATE_NONE; + + return o->decide_unicast_peer; + } break; + + case DECIDE_STATE_FLOOD: { + if (!o->decide_flood_current) { + o->decide_state = DECIDE_STATE_NONE; + return NULL; + } + + LinkedList1Node *list_node = o->decide_flood_current; + o->decide_flood_current = LinkedList1Node_Next(o->decide_flood_current); + + FrameDeciderPeer *peer = UPPER_OBJECT(list_node, FrameDeciderPeer, list_node); + + return peer; + } break; + + case DECIDE_STATE_MULTICAST: { + LinkedList3Node *list_node = LinkedList3Iterator_Next(&o->decide_multicast_it); + if (!list_node) { + o->decide_state = DECIDE_STATE_NONE; + return NULL; + } + struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node); + + return group_entry->peer; + } break; + + default: + ASSERT(0); + return NULL; + } +} + +int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc) +{ + // init arguments + o->d = d; + o->user = user; + o->logfunc = logfunc; + + // allocate MAC entries + if (!(o->mac_entries = (struct _FrameDecider_mac_entry *)BAllocArray(d->max_peer_macs, sizeof(struct _FrameDecider_mac_entry)))) { + PeerLog(o, BLOG_ERROR, "failed to allocate MAC entries"); + goto fail0; + } + + // allocate group entries + if (!(o->group_entries = (struct _FrameDecider_group_entry *)BAllocArray(d->max_peer_groups, sizeof(struct _FrameDecider_group_entry)))) { + PeerLog(o, BLOG_ERROR, "failed to allocate group entries"); + goto fail1; + } + + // insert to peers list + LinkedList1_Append(&d->peers_list, &o->list_node); + + // init MAC entry lists + LinkedList1_Init(&o->mac_entries_free); + LinkedList1_Init(&o->mac_entries_used); + + // initialize MAC entries + for (int i = 0; i < d->max_peer_macs; i++) { + struct _FrameDecider_mac_entry *entry = &o->mac_entries[i]; + + // set peer + entry->peer = o; + + // insert to free list + LinkedList1_Append(&o->mac_entries_free, &entry->list_node); + } + + // init group entry lists + LinkedList1_Init(&o->group_entries_free); + LinkedList1_Init(&o->group_entries_used); + + // initialize group entries + for (int i = 0; i < d->max_peer_groups; i++) { + struct _FrameDecider_group_entry *entry = &o->group_entries[i]; + + // set peer + entry->peer = o; + + // insert to free list + LinkedList1_Append(&o->group_entries_free, &entry->list_node); + + // init timer + BTimer_Init(&entry->timer, 0, (BTimer_handler)group_entry_timer_handler, entry); + } + + // initialize groups tree + FDGroupsTree_Init(&o->groups_tree); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + BFree(o->mac_entries); +fail0: + return 0; +} + +void FrameDeciderPeer_Free (FrameDeciderPeer *o) +{ + DebugObject_Free(&o->d_obj); + + FrameDecider *d = o->d; + + // remove decide unicast reference + if (d->decide_state == DECIDE_STATE_UNICAST && d->decide_unicast_peer == o) { + d->decide_state = DECIDE_STATE_NONE; + } + + LinkedList1Node *node; + + // free group entries + for (node = LinkedList1_GetFirst(&o->group_entries_used); node; node = LinkedList1Node_Next(node)) { + struct _FrameDecider_group_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from multicast + remove_from_multicast(d, entry); + + // stop timer + BReactor_RemoveTimer(d->reactor, &entry->timer); + } + + // remove used MAC entries from tree + for (node = LinkedList1_GetFirst(&o->mac_entries_used); node; node = LinkedList1Node_Next(node)) { + struct _FrameDecider_mac_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_mac_entry, list_node); + + // remove from tree + FDMacsTree_Remove(&d->macs_tree, 0, entry); + } + + // remove from peers list + if (d->decide_flood_current == &o->list_node) { + d->decide_flood_current = LinkedList1Node_Next(d->decide_flood_current); + } + LinkedList1_Remove(&d->peers_list, &o->list_node); + + // free group entries + BFree(o->group_entries); + + // free MAC entries + BFree(o->mac_entries); +} + +void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + DebugObject_Access(&o->d_obj); + + const uint8_t *pos = frame; + int len = frame_len; + + if (len < sizeof(struct ethernet_header)) { + goto out; + } + struct ethernet_header eh; + memcpy(&eh, pos, sizeof(eh)); + pos += sizeof(struct ethernet_header); + len -= sizeof(struct ethernet_header); + + // register source MAC address with this peer + add_mac_to_peer(o, eh.source); + + switch (ntoh16(eh.type)) { + case ETHERTYPE_IPV4: { + // check IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) { + PeerLog(o, BLOG_INFO, "analyze: wrong IP packet"); + goto out; + } + + // check if it's IGMP + if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) { + goto out; + } + + // check IGMP header + if (len < sizeof(struct igmp_base)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short packet"); + goto out; + } + struct igmp_base igmp_base; + memcpy(&igmp_base, pos, sizeof(igmp_base)); + pos += sizeof(struct igmp_base); + len -= sizeof(struct igmp_base); + + switch (ntoh8(igmp_base.type)) { + case IGMP_TYPE_V2_MEMBERSHIP_REPORT: { + // check extra + if (len < sizeof(struct igmp_v2_extra)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short v2 report"); + goto out; + } + struct igmp_v2_extra report; + memcpy(&report, pos, sizeof(report)); + pos += sizeof(struct igmp_v2_extra); + len -= sizeof(struct igmp_v2_extra); + + // add to group + add_group_to_peer(o, report.group); + } break; + + case IGMP_TYPE_V3_MEMBERSHIP_REPORT: { + // check extra + if (len < sizeof(struct igmp_v3_report_extra)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short v3 report"); + goto out; + } + struct igmp_v3_report_extra report; + memcpy(&report, pos, sizeof(report)); + pos += sizeof(struct igmp_v3_report_extra); + len -= sizeof(struct igmp_v3_report_extra); + + // iterate records + uint16_t num_records = ntoh16(report.number_of_group_records); + for (int i = 0; i < num_records; i++) { + // check record + if (len < sizeof(struct igmp_v3_report_record)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short record header"); + goto out; + } + struct igmp_v3_report_record record; + memcpy(&record, pos, sizeof(record)); + pos += sizeof(struct igmp_v3_report_record); + len -= sizeof(struct igmp_v3_report_record); + + // iterate sources + uint16_t num_sources = ntoh16(record.number_of_sources); + int j; + for (j = 0; j < num_sources; j++) { + // check source + if (len < sizeof(struct igmp_source)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short source"); + goto out; + } + pos += sizeof(struct igmp_source); + len -= sizeof(struct igmp_source); + } + + // check aux data + uint16_t aux_len = ntoh16(record.aux_data_len); + if (len < aux_len) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short record aux data"); + goto out; + } + pos += aux_len; + len -= aux_len; + + switch (record.type) { + case IGMP_RECORD_TYPE_MODE_IS_INCLUDE: + case IGMP_RECORD_TYPE_CHANGE_TO_INCLUDE_MODE: + if (num_sources != 0) { + add_group_to_peer(o, record.group); + } + break; + case IGMP_RECORD_TYPE_MODE_IS_EXCLUDE: + case IGMP_RECORD_TYPE_CHANGE_TO_EXCLUDE_MODE: + add_group_to_peer(o, record.group); + break; + } + } + } break; + } + } break; + } + +out:; +} diff --git a/app/src/main/jni/badvpn/client/FrameDecider.h b/app/src/main/jni/badvpn/client/FrameDecider.h new file mode 100644 index 0000000..f2a2937 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FrameDecider.h @@ -0,0 +1,196 @@ +/** + * @file FrameDecider.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mudule which decides to which peers frames from the device are to be + * forwarded. + */ + +#ifndef BADVPN_CLIENT_FRAMEDECIDER_H +#define BADVPN_CLIENT_FRAMEDECIDER_H + +#include + +#include +#include +#include +#include +#include +#include + +struct _FrameDeciderPeer; +struct _FrameDecider_mac_entry; +struct _FrameDecider_group_entry; + +typedef const uint8_t *FDMacsTree_key; + +#include "FrameDecider_macs_tree.h" +#include + +#include "FrameDecider_groups_tree.h" +#include + +#include "FrameDecider_multicast_tree.h" +#include + +struct _FrameDecider_mac_entry { + struct _FrameDeciderPeer *peer; + LinkedList1Node list_node; // node in FrameDeciderPeer.mac_entries_free or FrameDeciderPeer.mac_entries_used + // defined when used: + uint8_t mac[6]; + FDMacsTreeNode tree_node; // node in FrameDecider.macs_tree, indexed by mac +}; + +struct _FrameDecider_group_entry { + struct _FrameDeciderPeer *peer; + LinkedList1Node list_node; // node in FrameDeciderPeer.group_entries_free or FrameDeciderPeer.group_entries_used + BTimer timer; // timer for removing the group entry, running when used + // defined when used: + // basic group data + uint32_t group; // group address + FDGroupsTreeNode tree_node; // node in FrameDeciderPeer.groups_tree, indexed by group + // all that folows is managed by add_to_multicast() and remove_from_multicast() + LinkedList3Node sig_list_node; // node in list of group entries with the same sig + btime_t timer_endtime; + int is_master; + // defined when used and we are master: + struct { + uint32_t sig; // last 23 bits of group address + FDMulticastTreeNode tree_node; // node in FrameDecider.multicast_tree, indexed by sig + } master; +}; + +/** + * Object that represents a local device. + */ +typedef struct { + int max_peer_macs; + int max_peer_groups; + btime_t igmp_group_membership_interval; + btime_t igmp_last_member_query_time; + BReactor *reactor; + LinkedList1 peers_list; + FDMacsTree macs_tree; + FDMulticastTree multicast_tree; + int decide_state; + LinkedList1Node *decide_flood_current; + struct _FrameDeciderPeer *decide_unicast_peer; + LinkedList3Iterator decide_multicast_it; + DebugObject d_obj; +} FrameDecider; + +/** + * Object that represents a peer that a local device can send frames to. + */ +typedef struct _FrameDeciderPeer { + FrameDecider *d; + void *user; + BLog_logfunc logfunc; + struct _FrameDecider_mac_entry *mac_entries; + struct _FrameDecider_group_entry *group_entries; + LinkedList1Node list_node; // node in FrameDecider.peers_list + LinkedList1 mac_entries_free; + LinkedList1 mac_entries_used; + LinkedList1 group_entries_free; + LinkedList1 group_entries_used; + FDGroupsTree groups_tree; + DebugObject d_obj; +} FrameDeciderPeer; + +/** + * Initializes the object. + * + * @param o the object + * @param max_peer_macs maximum number of MAC addresses a peer may posess. Must be >0. + * @param max_peer_groups maximum number of multicast groups a peer may belong to. Must be >0. + * @param igmp_group_membership_interval IGMP Group Membership Interval value. When a join + * is detected for a peer in {@link FrameDeciderPeer_Analyze}, this is how long we wait + * for another join before we remove the group from the peer. Note that the group may + * be removed sooner if the peer fails to respond to a Group-Specific Query (see below). + * @param igmp_last_member_query_time IGMP Last Member Query Time value. When a Group-Specific + * Query is detected in {@link FrameDecider_AnalyzeAndDecide}, this is how long we wait for a peer + * belonging to the group to send a join before we remove the group from it. + */ +void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor); + +/** + * Frees the object. + * There must be no {@link FrameDeciderPeer} objects using this decider. + * + * @param o the object + */ +void FrameDecider_Free (FrameDecider *o); + +/** + * Analyzes a frame read from the local device and starts deciding which peers + * the frame should be forwarded to. + * + * @param o the object + * @param frame frame data + * @param frame_len frame length. Must be >=0. + */ +void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len); + +/** + * Returns the next peer that the frame submitted to {@link FrameDecider_AnalyzeAndDecide} should be + * forwarded to. + * + * @param o the object + * @return peer to forward the frame to, or NULL if no more + */ +FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o); + +/** + * Initializes the object. + * + * @param o the object + * @param d decider this peer will belong to + * @param user argument to log function + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void FrameDeciderPeer_Free (FrameDeciderPeer *o); + +/** + * Analyzes a frame received from the peer. + * + * @param o the object + * @param frame frame data + * @param frame_len frame length. Must be >=0. + */ +void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len); + +#endif diff --git a/app/src/main/jni/badvpn/client/FrameDecider_groups_tree.h b/app/src/main/jni/badvpn/client/FrameDecider_groups_tree.h new file mode 100644 index 0000000..b52a947 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FrameDecider_groups_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDGroupsTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry +#define SAVL_PARAM_TYPE_KEY uint32_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->group, (entry2)->group) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->group) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/app/src/main/jni/badvpn/client/FrameDecider_macs_tree.h b/app/src/main/jni/badvpn/client/FrameDecider_macs_tree.h new file mode 100644 index 0000000..2145918 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FrameDecider_macs_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDMacsTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_mac_entry +#define SAVL_PARAM_TYPE_KEY FDMacsTree_key +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_macs((entry1)->mac, (entry2)->mac) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) compare_macs((key1), (entry2)->mac) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/app/src/main/jni/badvpn/client/FrameDecider_multicast_tree.h b/app/src/main/jni/badvpn/client/FrameDecider_multicast_tree.h new file mode 100644 index 0000000..2731684 --- /dev/null +++ b/app/src/main/jni/badvpn/client/FrameDecider_multicast_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDMulticastTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry +#define SAVL_PARAM_TYPE_KEY uint32_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->master.sig, (entry2)->master.sig) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->master.sig) +#define SAVL_PARAM_MEMBER_NODE master.tree_node diff --git a/app/src/main/jni/badvpn/client/PasswordListener.c b/app/src/main/jni/badvpn/client/PasswordListener.c new file mode 100644 index 0000000..5ec573b --- /dev/null +++ b/app/src/main/jni/badvpn/client/PasswordListener.c @@ -0,0 +1,374 @@ +/** + * @file PasswordListener.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int password_comparator (void *user, uint64_t *p1, uint64_t *p2); +static void remove_client (struct PasswordListenerClient *client); +static void listener_handler (PasswordListener *l); +static void client_connection_handler (struct PasswordListenerClient *client, int event); +static void client_sslcon_handler (struct PasswordListenerClient *client, int event); +static void client_receiver_handler (struct PasswordListenerClient *client); + +int password_comparator (void *user, uint64_t *p1, uint64_t *p2) +{ + return B_COMPARE(*p1, *p2); +} + +void remove_client (struct PasswordListenerClient *client) +{ + PasswordListener *l = client->l; + + // stop using any buffers before they get freed + if (l->ssl) { + BSSLConnection_ReleaseBuffers(&client->sslcon); + } + + // free receiver + SingleStreamReceiver_Free(&client->receiver); + + // free SSL + if (l->ssl) { + BSSLConnection_Free(&client->sslcon); + ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS) + } + + // free connection interfaces + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + + // free connection + BConnection_Free(&client->sock->con); + + // free sslsocket structure + free(client->sock); + + // move to free list + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); +} + +void listener_handler (PasswordListener *l) +{ + DebugObject_Access(&l->d_obj); + + // obtain client entry + if (LinkedList1_IsEmpty(&l->clients_free)) { + struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetFirst(&l->clients_used), struct PasswordListenerClient, list_node); + remove_client(client); + } + struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetLast(&l->clients_free), struct PasswordListenerClient, list_node); + LinkedList1_Remove(&l->clients_free, &client->list_node); + LinkedList1_Append(&l->clients_used, &client->list_node); + + // allocate sslsocket structure + if (!(client->sock = (sslsocket *)malloc(sizeof(*client->sock)))) { + BLog(BLOG_ERROR, "malloc failedt"); + goto fail0; + } + + // accept connection + if (!BConnection_Init(&client->sock->con, BConnection_source_listener(&l->listener, NULL), l->bsys, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + BLog(BLOG_INFO, "Connection accepted"); + + // init connection interfaces + BConnection_SendAsync_Init(&client->sock->con); + BConnection_RecvAsync_Init(&client->sock->con); + + StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->sock->con); + StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->sock->con); + + if (l->ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&client->sock->bottom_prfd, send_if, recv_if, l->twd, l->ssl_flags)) { + BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(client->sock->ssl_prfd = SSL_ImportFD(l->model_prfd, &client->sock->bottom_prfd))) { + ASSERT_FORCE(PR_Close(&client->sock->bottom_prfd) == PR_SUCCESS) + goto fail2; + } + + // set server mode + if (SSL_ResetHandshake(client->sock->ssl_prfd, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + + // initialize SSLConnection + BSSLConnection_Init(&client->sslcon, client->sock->ssl_prfd, 0, BReactor_PendingGroup(l->bsys), client, (BSSLConnection_handler)client_sslcon_handler); + + send_if = BSSLConnection_GetSendIf(&client->sslcon); + recv_if = BSSLConnection_GetRecvIf(&client->sslcon); + } + + // init receiver + SingleStreamReceiver_Init(&client->receiver, (uint8_t *)&client->recv_buffer, sizeof(client->recv_buffer), recv_if, BReactor_PendingGroup(l->bsys), client, (SingleStreamReceiver_handler)client_receiver_handler); + + return; + + // cleanup on error +fail3: + if (l->ssl) { + ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS) + } +fail2: + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + BConnection_Free(&client->sock->con); +fail1: + free(client->sock); +fail0: + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); +} + +void client_connection_handler (struct PasswordListenerClient *client, int event) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "connection closed"); + } else { + BLog(BLOG_INFO, "connection error"); + } + + remove_client(client); +} + +void client_sslcon_handler (struct PasswordListenerClient *client, int event) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + ASSERT(l->ssl) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + BLog(BLOG_INFO, "SSL error"); + + remove_client(client); +} + +void client_receiver_handler (struct PasswordListenerClient *client) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + + // check password + uint64_t received_pass = ltoh64(client->recv_buffer); + BAVLNode *pw_tree_node = BAVL_LookupExact(&l->passwords, &received_pass); + if (!pw_tree_node) { + BLog(BLOG_WARNING, "unknown password"); + remove_client(client); + return; + } + PasswordListener_pwentry *pw_entry = UPPER_OBJECT(pw_tree_node, PasswordListener_pwentry, tree_node); + + BLog(BLOG_INFO, "Password recognized"); + + // remove password entry + BAVL_Remove(&l->passwords, &pw_entry->tree_node); + + // stop using any buffers before they get freed + if (l->ssl) { + BSSLConnection_ReleaseBuffers(&client->sslcon); + } + + // free receiver + SingleStreamReceiver_Free(&client->receiver); + + if (l->ssl) { + // free SSL connection + BSSLConnection_Free(&client->sslcon); + } else { + // free connection interfaces + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + } + + // remove connection handler + BConnection_SetHandlers(&client->sock->con, NULL, NULL); + + // move client entry to free list + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); + + // give the socket to the handler + pw_entry->handler_client(pw_entry->user, client->sock); + return; +} + +int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key) +{ + ASSERT(BConnection_AddressSupported(listen_addr)) + ASSERT(max_clients > 0) + ASSERT(ssl == 0 || ssl == 1) + + // init arguments + l->bsys = bsys; + l->twd = twd; + l->ssl = ssl; + l->ssl_flags = ssl_flags; + + // allocate client entries + if (!(l->clients_data = (struct PasswordListenerClient *)BAllocArray(max_clients, sizeof(struct PasswordListenerClient)))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + if (l->ssl) { + // initialize model SSL fd + DummyPRFileDesc_Create(&l->model_dprfd); + if (!(l->model_prfd = SSL_ImportFD(NULL, &l->model_dprfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&l->model_dprfd) == PR_SUCCESS) + goto fail1; + } + + // set server certificate + if (SSL_ConfigSecureServer(l->model_prfd, cert, key, NSS_FindCertKEAType(cert)) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail2; + } + } + + // initialize client entries + LinkedList1_Init(&l->clients_free); + LinkedList1_Init(&l->clients_used); + for (int i = 0; i < max_clients; i++) { + struct PasswordListenerClient *conn = &l->clients_data[i]; + conn->l = l; + LinkedList1_Append(&l->clients_free, &conn->list_node); + } + + // initialize passwords tree + BAVL_Init(&l->passwords, OFFSET_DIFF(PasswordListener_pwentry, password, tree_node), (BAVL_comparator)password_comparator, NULL); + + // initialize listener + if (!BListener_Init(&l->listener, listen_addr, l->bsys, l, (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail2; + } + + DebugObject_Init(&l->d_obj); + return 1; + + // cleanup +fail2: + if (l->ssl) { + ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS) + } +fail1: + BFree(l->clients_data); +fail0: + return 0; +} + +void PasswordListener_Free (PasswordListener *l) +{ + DebugObject_Free(&l->d_obj); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&l->clients_used)) { + struct PasswordListenerClient *client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node); + remove_client(client); + } + + // free listener + BListener_Free(&l->listener); + + // free model SSL file descriptor + if (l->ssl) { + ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS) + } + + // free client entries + BFree(l->clients_data); +} + +uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user) +{ + DebugObject_Access(&l->d_obj); + + while (1) { + // generate password + BRandom_randomize((uint8_t *)&entry->password, sizeof(entry->password)); + + // try inserting + if (BAVL_Insert(&l->passwords, &entry->tree_node, NULL)) { + break; + } + } + + entry->handler_client = handler_client; + entry->user = user; + + return entry->password; +} + +void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry) +{ + DebugObject_Access(&l->d_obj); + + // remove + BAVL_Remove(&l->passwords, &entry->tree_node); +} diff --git a/app/src/main/jni/badvpn/client/PasswordListener.h b/app/src/main/jni/badvpn/client/PasswordListener.h new file mode 100644 index 0000000..bbc0bd1 --- /dev/null +++ b/app/src/main/jni/badvpn/client/PasswordListener.h @@ -0,0 +1,156 @@ +/** + * @file PasswordListener.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used to listen on a socket, accept clients and identify them + * based on a number they send. + */ + +#ifndef BADVPN_CLIENT_PASSWORDLISTENER_H +#define BADVPN_CLIENT_PASSWORDLISTENER_H + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler function called when a client identifies itself with a password + * belonging to one of the password entries. + * The password entry is unregistered before the handler is called + * and must not be unregistered again. + * + * @param user as in {@link PasswordListener_AddEntry} + * @param sock structure containing a {@link BConnection} and, if TLS is enabled, + * the SSL socket with the bottom layer connected to the async interfaces + * of the {@link BConnection} object. The structure was allocated with + * malloc() and the user is responsible for freeing it. + */ +typedef void (*PasswordListener_handler_client) (void *user, sslsocket *sock); + +struct PasswordListenerClient; + +/** + * Object used to listen on a socket, accept clients and identify them + * based on a number they send. + */ +typedef struct { + BReactor *bsys; + BThreadWorkDispatcher *twd; + int ssl; + int ssl_flags; + PRFileDesc model_dprfd; + PRFileDesc *model_prfd; + struct PasswordListenerClient *clients_data; + LinkedList1 clients_free; + LinkedList1 clients_used; + BAVL passwords; + BListener listener; + DebugObject d_obj; +} PasswordListener; + +typedef struct { + uint64_t password; + BAVLNode tree_node; + PasswordListener_handler_client handler_client; + void *user; +} PasswordListener_pwentry; + +struct PasswordListenerClient { + PasswordListener *l; + LinkedList1Node list_node; + sslsocket *sock; + BSSLConnection sslcon; + SingleStreamReceiver receiver; + uint64_t recv_buffer; +}; + +/** + * Initializes the object. + * + * @param l the object + * @param bsys reactor we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param listen_addr address to listen on. Must be supported according to {@link BConnection_AddressSupported}. + * @param max_clients maximum number of client to hold until they are identified. + * Must be >0. + * @param ssl whether to use TLS. Must be 1 or 0. + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param cert if using TLS, the server certificate + * @param key if using TLS, the private key + * @return 1 on success, 0 on failure + */ +int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key) WARN_UNUSED; + +/** + * Frees the object. + * + * @param l the object + */ +void PasswordListener_Free (PasswordListener *l); + +/** + * Registers a password entry. + * + * @param l the object + * @param entry uninitialized entry structure + * @param handler_client handler function to call when a client identifies + * with the password which this function returns + * @param user value to pass to handler function + * @return password which a client should send to be recognized and + * dispatched to the handler function. Should be treated as a numeric + * value, which a client should as a little-endian 64-bit unsigned integer + * when it connects. + */ +uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user); + +/** + * Unregisters a password entry. + * Note that when a client is dispatched, its entry is unregistered + * automatically and must not be unregistered again here. + * + * @param l the object + * @param entry entry to unregister + */ +void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry); + +#endif diff --git a/app/src/main/jni/badvpn/client/PeerChat.c b/app/src/main/jni/badvpn/client/PeerChat.c new file mode 100644 index 0000000..d9dd966 --- /dev/null +++ b/app/src/main/jni/badvpn/client/PeerChat.c @@ -0,0 +1,433 @@ +/** + * @file PeerChat.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#include "PeerChat.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void report_error (PeerChat *o) +{ + DebugError_AssertNoError(&o->d_err); + + DEBUGERROR(&o->d_err, o->handler_error(o->user)) + return; +} + +static void recv_job_handler (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv_data_len >= 0) + ASSERT(o->recv_data_len <= SC_MAX_MSGLEN) + + int data_len = o->recv_data_len; + + // set no received data + o->recv_data_len = -1; + +#ifdef PEERCHAT_SIMULATE_ERROR + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < PEERCHAT_SIMULATE_ERROR) { + PeerLog(o, BLOG_ERROR, "simulate error"); + report_error(o); + return; + } +#endif + + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + // buffer data + if (!SimpleStreamBuffer_Write(&o->ssl_recv_buf, o->recv_data, data_len)) { + PeerLog(o, BLOG_ERROR, "out of recv buffer"); + report_error(o); + return; + } + } else { + // call message handler + o->handler_message(o->user, o->recv_data, data_len); + return; + } +} + +static void ssl_con_handler (PeerChat *o, int event) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + PeerLog(o, BLOG_ERROR, "SSL error"); + + report_error(o); + return; +} + +static SECStatus client_auth_data_callback (PeerChat *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT) + + CERTCertificate *cert = CERT_DupCertificate(o->ssl_cert); + if (!cert) { + PeerLog(o, BLOG_ERROR, "CERT_DupCertificate failed"); + goto fail0; + } + + SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(o->ssl_key); + if (!key) { + PeerLog(o, BLOG_ERROR, "SECKEY_CopyPrivateKey failed"); + goto fail1; + } + + *pRetCert = cert; + *pRetKey = key; + return SECSuccess; + +fail1: + CERT_DestroyCertificate(cert); +fail0: + return SECFailure; +} + +static SECStatus auth_certificate_callback (PeerChat *o, PRFileDesc *fd, PRBool checkSig, PRBool isServer) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + + // This callback is used to bypass checking the server's domain name, as peers + // don't have domain names. We byte-compare the certificate to the one reported + // by the server anyway. + + SECStatus ret = SECFailure; + + CERTCertificate *cert = SSL_PeerCertificate(o->ssl_prfd); + if (!cert) { + PeerLog(o, BLOG_ERROR, "SSL_PeerCertificate failed"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail1; + } + + SECCertUsage cert_usage = (o->ssl_mode == PEERCHAT_SSL_CLIENT ? certUsageSSLServer : certUsageSSLClient); + + if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), cert, PR_TRUE, cert_usage, SSL_RevealPinArg(o->ssl_prfd)) != SECSuccess) { + goto fail2; + } + + // compare to certificate provided by the server + SECItem der = cert->derCert; + if (der.len != o->ssl_peer_cert_len || memcmp(der.data, o->ssl_peer_cert, der.len)) { + PeerLog(o, BLOG_ERROR, "peer certificate doesn't match"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail2; + } + + ret = SECSuccess; + +fail2: + CERT_DestroyCertificate(cert); +fail1: + return ret; +} + +static void ssl_recv_if_handler_send (PeerChat *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // accept packet + PacketPassInterface_Done(&o->ssl_recv_if); + + // call message handler + o->handler_message(o->user, data, data_len); + return; +} + +static void ssl_recv_decoder_handler_error (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + + PeerLog(o, BLOG_ERROR, "decoder error"); + + report_error(o); + return; +} + +int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key, + uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, + BLog_logfunc logfunc, + PeerChat_handler_error handler_error, + PeerChat_handler_message handler_message) +{ + ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_mode == PEERCHAT_SSL_CLIENT || ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_peer_cert_len >= 0) + ASSERT(logfunc) + ASSERT(handler_error) + ASSERT(handler_message) + + // init arguments + o->ssl_mode = ssl_mode; + o->ssl_cert = ssl_cert; + o->ssl_key = ssl_key; + o->ssl_peer_cert = ssl_peer_cert; + o->ssl_peer_cert_len = ssl_peer_cert_len; + o->user = user; + o->logfunc = logfunc; + o->handler_error = handler_error; + o->handler_message = handler_message; + + // init copier + PacketCopier_Init(&o->copier, SC_MAX_MSGLEN, pg); + + // init SC encoder + SCOutmsgEncoder_Init(&o->sc_encoder, peer_id, PacketCopier_GetOutput(&o->copier), pg); + + // init PacketProto encoder + PacketProtoEncoder_Init(&o->pp_encoder, SCOutmsgEncoder_GetOutput(&o->sc_encoder), pg); + + // init recv job + BPending_Init(&o->recv_job, pg, (BPending_handler)recv_job_handler, o); + + // set no received data + o->recv_data_len = -1; + + PacketPassInterface *send_buf_output = PacketCopier_GetInput(&o->copier); + + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + // init receive buffer + if (!SimpleStreamBuffer_Init(&o->ssl_recv_buf, PEERCHAT_SSL_RECV_BUF_SIZE, pg)) { + PeerLog(o, BLOG_ERROR, "SimpleStreamBuffer_Init failed"); + goto fail1; + } + + // init SSL StreamPacketSender + StreamPacketSender_Init(&o->ssl_sp_sender, send_buf_output, pg); + + // init SSL bottom prfd + if (!BSSLConnection_MakeBackend(&o->ssl_bottom_prfd, StreamPacketSender_GetInput(&o->ssl_sp_sender), SimpleStreamBuffer_GetOutput(&o->ssl_recv_buf), twd, ssl_flags)) { + PeerLog(o, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // init SSL prfd + if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->ssl_bottom_prfd))) { + ASSERT_FORCE(PR_Close(&o->ssl_bottom_prfd) == PR_SUCCESS) + PeerLog(o, BLOG_ERROR, "SSL_ImportFD failed"); + goto fail2; + } + + // set client or server mode + if (SSL_ResetHandshake(o->ssl_prfd, (o->ssl_mode == PEERCHAT_SSL_SERVER ? PR_TRUE : PR_FALSE)) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + if (o->ssl_mode == PEERCHAT_SSL_SERVER) { + // set server certificate + if (SSL_ConfigSecureServer(o->ssl_prfd, o->ssl_cert, o->ssl_key, NSS_FindCertKEAType(o->ssl_cert)) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(o->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(o->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + } else { + // set client certificate callback + if (SSL_GetClientAuthDataHook(o->ssl_prfd, (SSLGetClientAuthData)client_auth_data_callback, o) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail3; + } + } + + // set verify peer certificate hook + if (SSL_AuthCertificateHook(o->ssl_prfd, (SSLAuthCertificate)auth_certificate_callback, o) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail3; + } + + // init SSL connection + BSSLConnection_Init(&o->ssl_con, o->ssl_prfd, 0, pg, o, (BSSLConnection_handler)ssl_con_handler); + + // init SSL PacketStreamSender + PacketStreamSender_Init(&o->ssl_ps_sender, BSSLConnection_GetSendIf(&o->ssl_con), sizeof(struct packetproto_header) + SC_MAX_MSGLEN, pg); + + // init SSL copier + PacketCopier_Init(&o->ssl_copier, SC_MAX_MSGLEN, pg); + + // init SSL encoder + PacketProtoEncoder_Init(&o->ssl_encoder, PacketCopier_GetOutput(&o->ssl_copier), pg); + + // init SSL buffer + if (!SinglePacketBuffer_Init(&o->ssl_buffer, PacketProtoEncoder_GetOutput(&o->ssl_encoder), PacketStreamSender_GetInput(&o->ssl_ps_sender), pg)) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + // init receive interface + PacketPassInterface_Init(&o->ssl_recv_if, SC_MAX_MSGLEN, (PacketPassInterface_handler_send)ssl_recv_if_handler_send, o, pg); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->ssl_recv_decoder, BSSLConnection_GetRecvIf(&o->ssl_con), &o->ssl_recv_if, pg, o, (PacketProtoDecoder_handler_error)ssl_recv_decoder_handler_error)) { + PeerLog(o, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail5; + } + + send_buf_output = PacketCopier_GetInput(&o->ssl_copier); + } + + // init send writer + BufferWriter_Init(&o->send_writer, SC_MAX_MSGLEN, pg); + + // init send buffer + if (!PacketBuffer_Init(&o->send_buf, BufferWriter_GetOutput(&o->send_writer), send_buf_output, PEERCHAT_SEND_BUF_SIZE, pg)) { + PeerLog(o, BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail6; + } + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); + return 1; + +fail6: + BufferWriter_Free(&o->send_writer); + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + PacketProtoDecoder_Free(&o->ssl_recv_decoder); +fail5: + PacketPassInterface_Free(&o->ssl_recv_if); + SinglePacketBuffer_Free(&o->ssl_buffer); +fail4: + PacketProtoEncoder_Free(&o->ssl_encoder); + PacketCopier_Free(&o->ssl_copier); + PacketStreamSender_Free(&o->ssl_ps_sender); + BSSLConnection_Free(&o->ssl_con); +fail3: + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) +fail2: + StreamPacketSender_Free(&o->ssl_sp_sender); + SimpleStreamBuffer_Free(&o->ssl_recv_buf); + } +fail1: + BPending_Free(&o->recv_job); + PacketProtoEncoder_Free(&o->pp_encoder); + SCOutmsgEncoder_Free(&o->sc_encoder); + PacketCopier_Free(&o->copier); + return 0; +} + +void PeerChat_Free (PeerChat *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // stop using any buffers before they get freed + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + BSSLConnection_ReleaseBuffers(&o->ssl_con); + } + + PacketBuffer_Free(&o->send_buf); + BufferWriter_Free(&o->send_writer); + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + PacketProtoDecoder_Free(&o->ssl_recv_decoder); + PacketPassInterface_Free(&o->ssl_recv_if); + SinglePacketBuffer_Free(&o->ssl_buffer); + PacketProtoEncoder_Free(&o->ssl_encoder); + PacketCopier_Free(&o->ssl_copier); + PacketStreamSender_Free(&o->ssl_ps_sender); + BSSLConnection_Free(&o->ssl_con); + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + StreamPacketSender_Free(&o->ssl_sp_sender); + SimpleStreamBuffer_Free(&o->ssl_recv_buf); + } + BPending_Free(&o->recv_job); + PacketProtoEncoder_Free(&o->pp_encoder); + SCOutmsgEncoder_Free(&o->sc_encoder); + PacketCopier_Free(&o->copier); +} + +PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + + return PacketProtoEncoder_GetOutput(&o->pp_encoder); +} + +void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv_data_len == -1) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // remember data + o->recv_data = data; + o->recv_data_len = data_len; + + // set received job + BPending_Set(&o->recv_job); +} + +int PeerChat_StartMessage (PeerChat *o, uint8_t **data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + return BufferWriter_StartPacket(&o->send_writer, data); +} + +void PeerChat_EndMessage (PeerChat *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + BufferWriter_EndPacket(&o->send_writer, data_len); +} diff --git a/app/src/main/jni/badvpn/client/PeerChat.h b/app/src/main/jni/badvpn/client/PeerChat.h new file mode 100644 index 0000000..674e374 --- /dev/null +++ b/app/src/main/jni/badvpn/client/PeerChat.h @@ -0,0 +1,123 @@ +/** + * @file PeerChat.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PEERCHAT_H +#define BADVPN_PEERCHAT_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEERCHAT_SSL_NONE 0 +#define PEERCHAT_SSL_CLIENT 1 +#define PEERCHAT_SSL_SERVER 2 + +#define PEERCHAT_SSL_RECV_BUF_SIZE 4096 +#define PEERCHAT_SEND_BUF_SIZE 200 + +//#define PEERCHAT_SIMULATE_ERROR 40 + +typedef void (*PeerChat_handler_error) (void *user); +typedef void (*PeerChat_handler_message) (void *user, uint8_t *data, int data_len); + +typedef struct { + int ssl_mode; + CERTCertificate *ssl_cert; + SECKEYPrivateKey *ssl_key; + uint8_t *ssl_peer_cert; + int ssl_peer_cert_len; + void *user; + BLog_logfunc logfunc; + PeerChat_handler_error handler_error; + PeerChat_handler_message handler_message; + + // transport + PacketProtoEncoder pp_encoder; + SCOutmsgEncoder sc_encoder; + PacketCopier copier; + BPending recv_job; + uint8_t *recv_data; + int recv_data_len; + + // SSL transport + StreamPacketSender ssl_sp_sender; + SimpleStreamBuffer ssl_recv_buf; + + // SSL connection + PRFileDesc ssl_bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection ssl_con; + + // SSL higher layer + PacketStreamSender ssl_ps_sender; + SinglePacketBuffer ssl_buffer; + PacketProtoEncoder ssl_encoder; + PacketCopier ssl_copier; + PacketProtoDecoder ssl_recv_decoder; + PacketPassInterface ssl_recv_if; + + // higher layer send buffer + PacketBuffer send_buf; + BufferWriter send_writer; + + DebugError d_err; + DebugObject d_obj; +} PeerChat; + +int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key, + uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, + BLog_logfunc logfunc, + PeerChat_handler_error handler_error, + PeerChat_handler_message handler_message) WARN_UNUSED; +void PeerChat_Free (PeerChat *o); +PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o); +void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len); +int PeerChat_StartMessage (PeerChat *o, uint8_t **data) WARN_UNUSED; +void PeerChat_EndMessage (PeerChat *o, int data_len); + +#endif diff --git a/app/src/main/jni/badvpn/client/SCOutmsgEncoder.c b/app/src/main/jni/badvpn/client/SCOutmsgEncoder.c new file mode 100644 index 0000000..83e8b27 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SCOutmsgEncoder.c @@ -0,0 +1,104 @@ +/** + * @file SCOutmsgEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "SCOutmsgEncoder.h" + +static void output_handler_recv (SCOutmsgEncoder *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->output_packet) + ASSERT(data) + + // schedule receive + o->output_packet = data; + PacketRecvInterface_Receiver_Recv(o->input, o->output_packet + SCOUTMSG_OVERHEAD); +} + +static void input_handler_done (SCOutmsgEncoder *o, int in_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_packet) + + // write SC header + struct sc_header header; + header.type = htol8(SCID_OUTMSG); + memcpy(o->output_packet, &header, sizeof(header)); + + // write outmsg + struct sc_client_outmsg outmsg; + outmsg.clientid = htol16(o->peer_id); + memcpy(o->output_packet + sizeof(header), &outmsg, sizeof(outmsg)); + + // finish output packet + o->output_packet = NULL; + PacketRecvInterface_Done(&o->output, SCOUTMSG_OVERHEAD + in_len); +} + +void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - SCOUTMSG_OVERHEAD) + + // init arguments + o->peer_id = peer_id; + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketRecvInterface_Init(&o->output, SCOUTMSG_OVERHEAD + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no output packet + o->output_packet = NULL; + + DebugObject_Init(&o->d_obj); +} + +void SCOutmsgEncoder_Free (SCOutmsgEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/client/SCOutmsgEncoder.h b/app/src/main/jni/badvpn/client/SCOutmsgEncoder.h new file mode 100644 index 0000000..05d4cb2 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SCOutmsgEncoder.h @@ -0,0 +1,76 @@ +/** + * @file SCOutmsgEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SCOUTMSGENCODER_H +#define BADVPN_SCOUTMSGENCODER_H + +#include +#include +#include + +#define SCOUTMSG_OVERHEAD (sizeof(struct sc_header) + sizeof(struct sc_client_outmsg)) + +/** + * A {@link PacketRecvInterface} layer which encodes SCProto outgoing messages. + */ +typedef struct { + peerid_t peer_id; + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *output_packet; + DebugObject d_obj; +} SCOutmsgEncoder; + +/** + * Initializes the object. + * + * @param o the object + * @param peer_id destination peer for messages + * @param input input interface. Its MTU muse be <= (INT_MAX - SCOUTMSG_OVERHEAD). + * @param pg pending group we live in + */ +void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SCOutmsgEncoder_Free (SCOutmsgEncoder *o); + +/** + * Returns the output interface. + * The MTU of the interface will be (SCOUTMSG_OVERHEAD + input MTU). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/SPProtoDecoder.c b/app/src/main/jni/badvpn/client/SPProtoDecoder.c new file mode 100644 index 0000000..0855162 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SPProtoDecoder.c @@ -0,0 +1,398 @@ +/** + * @file SPProtoDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "SPProtoDecoder.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void decode_work_func (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->in_len <= o->input_mtu) + + uint8_t *in = o->in; + int in_len = o->in_len; + + o->tw_out_len = -1; + + uint8_t *plaintext; + int plaintext_len; + + // decrypt if needed + if (!SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + plaintext = in; + plaintext_len = in_len; + } else { + // input must be a multiple of blocks size + if (in_len % o->enc_block_size != 0) { + PeerLog(o, BLOG_WARNING, "packet size not a multiple of block size"); + return; + } + + // input must have an IV block + if (in_len < o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet does not have an IV"); + return; + } + + // check if we have encryption key + if (!o->have_encryption_key) { + PeerLog(o, BLOG_WARNING, "have no encryption key"); + return; + } + + // copy IV as BEncryption_Decrypt changes the IV + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv, in, o->enc_block_size); + + // decrypt + uint8_t *ciphertext = in + o->enc_block_size; + int ciphertext_len = in_len - o->enc_block_size; + plaintext = o->buf; + BEncryption_Decrypt(&o->encryptor, ciphertext, plaintext, ciphertext_len, iv); + + // read padding + if (ciphertext_len < o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet does not have a padding block"); + return; + } + int i; + for (i = ciphertext_len - 1; i >= ciphertext_len - o->enc_block_size; i--) { + if (plaintext[i] == 1) { + break; + } + if (plaintext[i] != 0) { + PeerLog(o, BLOG_WARNING, "packet padding wrong (nonzero byte)"); + return; + } + } + if (i < ciphertext_len - o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet padding wrong (all zeroes)"); + return; + } + plaintext_len = i; + } + + // check for header + if (plaintext_len < SPPROTO_HEADER_LEN(o->sp_params)) { + PeerLog(o, BLOG_WARNING, "packet has no header"); + return; + } + uint8_t *header = plaintext; + + // check data length + if (plaintext_len - SPPROTO_HEADER_LEN(o->sp_params) > o->output_mtu) { + PeerLog(o, BLOG_WARNING, "packet too long"); + return; + } + + // check OTP + if (SPPROTO_HAVE_OTP(o->sp_params)) { + // remember seed and OTP (can't check from here) + struct spproto_otpdata header_otpd; + memcpy(&header_otpd, header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), sizeof(header_otpd)); + o->tw_out_seed_id = ltoh16(header_otpd.seed_id); + o->tw_out_otp = header_otpd.otp; + } + + // check hash + if (SPPROTO_HAVE_HASH(o->sp_params)) { + uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params); + // read hash + uint8_t hash[BHASH_MAX_SIZE]; + memcpy(hash, header_hash, o->hash_size); + // zero hash in packet + memset(header_hash, 0, o->hash_size); + // calculate hash + uint8_t hash_calc[BHASH_MAX_SIZE]; + BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash_calc); + // set hash field to its original value + memcpy(header_hash, hash, o->hash_size); + // compare hashes + if (memcmp(hash, hash_calc, o->hash_size)) { + PeerLog(o, BLOG_WARNING, "packet has wrong hash"); + return; + } + } + + // return packet + o->tw_out = plaintext + SPPROTO_HEADER_LEN(o->sp_params); + o->tw_out_len = plaintext_len - SPPROTO_HEADER_LEN(o->sp_params); +} + +static void decode_work_handler (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->tw_have) + DebugObject_Access(&o->d_obj); + + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // check OTP + if (SPPROTO_HAVE_OTP(o->sp_params) && o->tw_out_len >= 0) { + if (!OTPChecker_CheckOTP(&o->otpchecker, o->tw_out_seed_id, o->tw_out_otp)) { + PeerLog(o, BLOG_WARNING, "packet has wrong OTP"); + o->tw_out_len = -1; + } + } + + if (o->tw_out_len < 0) { + // cannot decode, finish input packet + PacketPassInterface_Done(&o->input); + o->in_len = -1; + } else { + // submit decoded packet to output + PacketPassInterface_Sender_Send(o->output, o->tw_out, o->tw_out_len); + } +} + +static void input_handler_send (SPProtoDecoder *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember input + o->in = data; + o->in_len = data_len; + + // start decoding + BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)decode_work_handler, o, (BThreadWork_work_func)decode_work_func, o); + o->tw_have = 1; +} + +static void output_handler_done (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // finish input packet + PacketPassInterface_Done(&o->input); + o->in_len = -1; +} + +static void maybe_stop_work_and_ignore (SPProtoDecoder *o) +{ + ASSERT(!(o->tw_have) || o->in_len >= 0) + + if (o->tw_have) { + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // ignore packet, receive next one + PacketPassInterface_Done(&o->input); + o->in_len = -1; + } +} + +int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc) +{ + spproto_assert_security_params(sp_params); + ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketPassInterface_GetMTU(output)) >= 0) + ASSERT(!SPPROTO_HAVE_OTP(sp_params) || num_otp_seeds >= 2) + + // init arguments + o->output = output; + o->sp_params = sp_params; + o->twd = twd; + o->user = user; + o->logfunc = logfunc; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // remember output MTU + o->output_mtu = PacketPassInterface_GetMTU(o->output); + + // calculate hash size + if (SPPROTO_HAVE_HASH(o->sp_params)) { + o->hash_size = BHash_size(o->sp_params.hash_mode); + } + + // calculate encryption block and key sizes + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode); + o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode); + } + + // calculate input MTU + o->input_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->output_mtu); + + // allocate plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->output_mtu + 1), o->enc_block_size); + if (!(o->buf = (uint8_t *)malloc(buf_size))) { + goto fail0; + } + } + + // init input + PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // init OTP checker + if (SPPROTO_HAVE_OTP(o->sp_params)) { + if (!OTPChecker_Init(&o->otpchecker, o->sp_params.otp_num, o->sp_params.otp_mode, num_otp_seeds, o->twd)) { + goto fail1; + } + } + + // have no encryption key + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->have_encryption_key = 0; + } + + // have no input packet + o->in_len = -1; + + // have no work + o->tw_have = 0; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketPassInterface_Free(&o->input); + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } +fail0: + return 0; +} + +void SPProtoDecoder_Free (SPProtoDecoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + } + + // free encryptor + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // free OTP checker + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPChecker_Free(&o->otpchecker); + } + + // free input + PacketPassInterface_Free(&o->input); + + // free plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } +} + +PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work_and_ignore(o); + + // free encryptor + if (o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // init encryptor + BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_DECRYPT, o->sp_params.encryption_mode, encryption_key); + + // have encryption key + o->have_encryption_key = 1; +} + +void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work_and_ignore(o); + + if (o->have_encryption_key) { + // free encryptor + BEncryption_Free(&o->encryptor); + + // have no encryption key + o->have_encryption_key = 0; + } +} + +void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + OTPChecker_AddSeed(&o->otpchecker, seed_id, key, iv); +} + +void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + OTPChecker_RemoveSeeds(&o->otpchecker); +} + +void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPChecker_SetHandlers(&o->otpchecker, otp_handler, user); + } +} diff --git a/app/src/main/jni/badvpn/client/SPProtoDecoder.h b/app/src/main/jni/badvpn/client/SPProtoDecoder.h new file mode 100644 index 0000000..3b5de71 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SPProtoDecoder.h @@ -0,0 +1,171 @@ +/** + * @file SPProtoDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes packets according to SPProto. + */ + +#ifndef BADVPN_CLIENT_SPPROTODECODER_H +#define BADVPN_CLIENT_SPPROTODECODER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler called when OTP generation for a new seed is finished. + * + * @param user as in {@link SPProtoDecoder_Init} + */ +typedef void (*SPProtoDecoder_otp_handler) (void *user); + +/** + * Object which decodes packets according to SPProto. + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + PacketPassInterface *output; + struct spproto_security_params sp_params; + BThreadWorkDispatcher *twd; + void *user; + BLog_logfunc logfunc; + int output_mtu; + int hash_size; + int enc_block_size; + int enc_key_size; + int input_mtu; + uint8_t *buf; + PacketPassInterface input; + OTPChecker otpchecker; + int have_encryption_key; + BEncryption encryptor; + uint8_t *in; + int in_len; + int tw_have; + BThreadWork tw; + uint16_t tw_out_seed_id; + otp_t tw_out_otp; + uint8_t *tw_out; + int tw_out_len; + DebugObject d_obj; +} SPProtoDecoder; + +/** + * Initializes the object. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param output output interface. Its MTU must not be too large, i.e. this must hold: + * spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU) >= 0 + * @param sp_params SPProto parameters + * @param encryption_key if using encryption, the encryption key + * @param num_otp_seeds if using OTPs, how many OTP seeds to keep for checking + * receiving packets. Must be >=2 if using OTPs. + * @param pg pending group + * @param twd thread work dispatcher + * @param user argument to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void SPProtoDecoder_Free (SPProtoDecoder *o); + +/** + * Returns the input interface. + * The MTU of the input interface will depend on the output MTU and security parameters, + * that is spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU). + * + * @param o the object + * @return input interface + */ +PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o); + +/** + * Sets an encryption key for decrypting packets. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key); + +/** + * Removes an encryption key if one is configured. + * Encryption must be enabled. + * + * @param o the object + */ +void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o); + +/** + * Starts generating OTPs for a seed to check received packets against. + * OTPs for this seed will not be recognized until the {@link SPProtoDecoder_otp_handler} handler + * is called. + * If OTPs are still being generated for the previous seed, it will be forgotten. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all OTP seeds for checking received packets against. + * OTPs must be enabled. + * + * @param o the object + */ +void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o); + +/** + * Sets handlers. + * + * @param o the object + * @param otp_handler handler called when OTP generation is finished + * @param user argument to handler + */ +void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user); + +#endif diff --git a/app/src/main/jni/badvpn/client/SPProtoEncoder.c b/app/src/main/jni/badvpn/client/SPProtoEncoder.c new file mode 100644 index 0000000..fbbab50 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SPProtoEncoder.c @@ -0,0 +1,436 @@ +/** + * @file SPProtoEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "SPProtoEncoder.h" + +static int can_encode (SPProtoEncoder *o); +static void encode_packet (SPProtoEncoder *o); +static void encode_work_func (SPProtoEncoder *o); +static void encode_work_handler (SPProtoEncoder *o); +static void maybe_encode (SPProtoEncoder *o); +static void output_handler_recv (SPProtoEncoder *o, uint8_t *data); +static void input_handler_done (SPProtoEncoder *o, int data_len); +static void handler_job_hander (SPProtoEncoder *o); +static void otpgenerator_handler (SPProtoEncoder *o); +static void maybe_stop_work (SPProtoEncoder *o); + +static int can_encode (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + + return ( + (!SPPROTO_HAVE_OTP(o->sp_params) || OTPGenerator_GetPosition(&o->otpgen) < o->sp_params.otp_num) && + (!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key) + ); +} + +static void encode_packet (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + ASSERT(can_encode(o)) + + // generate OTP, remember seed ID + if (SPPROTO_HAVE_OTP(o->sp_params)) { + o->tw_seed_id = o->otpgen_seed_id; + o->tw_otp = OTPGenerator_GetOTP(&o->otpgen); + } + + // start work + BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)encode_work_handler, o, (BThreadWork_work_func)encode_work_func, o); + o->tw_have = 1; + + // schedule OTP warning handler + if (SPPROTO_HAVE_OTP(o->sp_params) && OTPGenerator_GetPosition(&o->otpgen) == o->otp_warning_count) { + BPending_Set(&o->handler_job); + } +} + +static void encode_work_func (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key) + + ASSERT(o->in_len <= o->input_mtu) + + // determine plaintext location + uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out); + + // plaintext begins with header + uint8_t *header = plaintext; + + // plaintext is header + payload + int plaintext_len = SPPROTO_HEADER_LEN(o->sp_params) + o->in_len; + + // write OTP + if (SPPROTO_HAVE_OTP(o->sp_params)) { + struct spproto_otpdata header_otpd; + header_otpd.seed_id = htol16(o->tw_seed_id); + header_otpd.otp = o->tw_otp; + memcpy(header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), &header_otpd, sizeof(header_otpd)); + } + + // write hash + if (SPPROTO_HAVE_HASH(o->sp_params)) { + uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params); + // zero hash field + memset(header_hash, 0, o->hash_size); + // calculate hash + uint8_t hash[BHASH_MAX_SIZE]; + BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash); + // set hash field + memcpy(header_hash, hash, o->hash_size); + } + + int out_len; + + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + // encrypting pad(header + payload) + int cyphertext_len = balign_up((plaintext_len + 1), o->enc_block_size); + + // write padding + plaintext[plaintext_len] = 1; + for (int i = plaintext_len + 1; i < cyphertext_len; i++) { + plaintext[i] = 0; + } + + // generate IV + BRandom_randomize(o->out, o->enc_block_size); + + // copy IV because BEncryption_Encrypt changes the IV + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv, o->out, o->enc_block_size); + + // encrypt + BEncryption_Encrypt(&o->encryptor, plaintext, o->out + o->enc_block_size, cyphertext_len, iv); + out_len = o->enc_block_size + cyphertext_len; + } else { + out_len = plaintext_len; + } + + // remember length + o->tw_out_len = out_len; +} + +static void encode_work_handler (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(o->tw_have) + + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // finish packet + o->in_len = -1; + o->out_have = 0; + PacketRecvInterface_Done(&o->output, o->tw_out_len); +} + +static void maybe_encode (SPProtoEncoder *o) +{ + if (o->in_len >= 0 && o->out_have && !o->tw_have && can_encode(o)) { + encode_packet(o); + } +} + +static void output_handler_recv (SPProtoEncoder *o, uint8_t *data) +{ + ASSERT(o->in_len == -1) + ASSERT(!o->out_have) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_have = 1; + o->out = data; + + // determine plaintext location + uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out); + + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, plaintext + SPPROTO_HEADER_LEN(o->sp_params)); +} + +static void input_handler_done (SPProtoEncoder *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + + // encode if possible + if (can_encode(o)) { + encode_packet(o); + } +} + +static void handler_job_hander (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + if (o->handler) { + o->handler(o->user); + return; + } +} + +static void otpgenerator_handler (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remember seed ID + o->otpgen_seed_id = o->otpgen_pending_seed_id; + + // possibly continue I/O + maybe_encode(o); +} + +static void maybe_stop_work (SPProtoEncoder *o) +{ + // stop existing work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + o->tw_have = 0; + } +} + +int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd) +{ + spproto_assert_security_params(sp_params); + ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketRecvInterface_GetMTU(input)) >= 0) + if (SPPROTO_HAVE_OTP(sp_params)) { + ASSERT(otp_warning_count > 0) + ASSERT(otp_warning_count <= sp_params.otp_num) + } + + // init arguments + o->input = input; + o->sp_params = sp_params; + o->otp_warning_count = otp_warning_count; + o->twd = twd; + + // set no handlers + o->handler = NULL; + + // calculate hash size + if (SPPROTO_HAVE_HASH(o->sp_params)) { + o->hash_size = BHash_size(o->sp_params.hash_mode); + } + + // calculate encryption block and key sizes + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode); + o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode); + } + + // init otp generator + if (SPPROTO_HAVE_OTP(o->sp_params)) { + if (!OTPGenerator_Init(&o->otpgen, o->sp_params.otp_num, o->sp_params.otp_mode, o->twd, (OTPGenerator_handler)otpgenerator_handler, o)) { + goto fail0; + } + } + + // have no encryption key + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->have_encryption_key = 0; + } + + // remember input MTU + o->input_mtu = PacketRecvInterface_GetMTU(o->input); + + // calculate output MTU + o->output_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->input_mtu); + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // have no input in buffer + o->in_len = -1; + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output available + o->out_have = 0; + + // allocate plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->input_mtu + 1), o->enc_block_size); + if (!(o->buf = (uint8_t *)malloc(buf_size))) { + goto fail1; + } + } + + // init handler job + BPending_Init(&o->handler_job, pg, (BPending_handler)handler_job_hander, o); + + // have no work + o->tw_have = 0; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketRecvInterface_Free(&o->output); + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPGenerator_Free(&o->otpgen); + } +fail0: + return 0; +} + +void SPProtoEncoder_Free (SPProtoEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + } + + // free handler job + BPending_Free(&o->handler_job); + + // free plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } + + // free output + PacketRecvInterface_Free(&o->output); + + // free encryptor + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // free otp generator + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPGenerator_Free(&o->otpgen); + } +} + +PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work(o); + + // free encryptor + if (o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // init encryptor + BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_ENCRYPT, o->sp_params.encryption_mode, encryption_key); + + // have encryption key + o->have_encryption_key = 1; + + // possibly continue I/O + maybe_encode(o); +} + +void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work(o); + + if (o->have_encryption_key) { + // free encryptor + BEncryption_Free(&o->encryptor); + + // have no encryption key + o->have_encryption_key = 0; + } +} + +void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // give seed to OTP generator + OTPGenerator_SetSeed(&o->otpgen, key, iv); + + // remember seed ID + o->otpgen_pending_seed_id = seed_id; +} + +void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // reset OTP generator + OTPGenerator_Reset(&o->otpgen); +} + +void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->user = user; +} diff --git a/app/src/main/jni/badvpn/client/SPProtoEncoder.h b/app/src/main/jni/badvpn/client/SPProtoEncoder.h new file mode 100644 index 0000000..874f391 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SPProtoEncoder.h @@ -0,0 +1,172 @@ +/** + * @file SPProtoEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets according to SPProto. + */ + +#ifndef BADVPN_CLIENT_SPPROTOENCODER_H +#define BADVPN_CLIENT_SPPROTOENCODER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Event context handler called when the remaining number of + * OTPs equals the warning number after having encoded a packet. + * + * @param user as in {@link SPProtoEncoder_Init} + */ +typedef void (*SPProtoEncoder_handler) (void *user); + +/** + * Object which encodes packets according to SPProto. + * + * Input is with {@link PacketRecvInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + PacketRecvInterface *input; + struct spproto_security_params sp_params; + int otp_warning_count; + SPProtoEncoder_handler handler; + BThreadWorkDispatcher *twd; + void *user; + int hash_size; + int enc_block_size; + int enc_key_size; + OTPGenerator otpgen; + uint16_t otpgen_seed_id; + uint16_t otpgen_pending_seed_id; + int have_encryption_key; + BEncryption encryptor; + int input_mtu; + int output_mtu; + int in_len; + PacketRecvInterface output; + int out_have; + uint8_t *out; + uint8_t *buf; + BPending handler_job; + int tw_have; + BThreadWork tw; + uint16_t tw_seed_id; + otp_t tw_otp; + int tw_out_len; + DebugObject d_obj; +} SPProtoEncoder; + +/** + * Initializes the object. + * The object is initialized in blocked state. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param input input interface. Its MTU must not be too large, i.e. this must hold: + * spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU) >= 0 + * @param sp_params SPProto security parameters + * @param otp_warning_count If using OTPs, after how many encoded packets to call the handler. + * In this case, must be >0 and <=sp_params.otp_num. + * @param pg pending group + * @param twd thread work dispatcher + * @return 1 on success, 0 on failure + */ +int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void SPProtoEncoder_Free (SPProtoEncoder *o); + +/** + * Returns the output interface. + * The MTU of the output interface will depend on the input MTU and security parameters, + * that is spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o); + +/** + * Sets an encryption key to use. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key); + +/** + * Removes an encryption key if one is configured. + * Encryption must be enabled. + * + * @param o the object + */ +void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o); + +/** + * Sets an OTP seed to use. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes the OTP seed if one is configured. + * OTPs must be enabled. + * + * @param o the object + */ +void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o); + +/** + * Sets handlers. + * + * @param o the object + * @param handler OTP warning handler + * @param user value to pass to handler + */ +void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user); + +#endif diff --git a/app/src/main/jni/badvpn/client/SimpleStreamBuffer.c b/app/src/main/jni/badvpn/client/SimpleStreamBuffer.c new file mode 100644 index 0000000..74448cb --- /dev/null +++ b/app/src/main/jni/badvpn/client/SimpleStreamBuffer.c @@ -0,0 +1,144 @@ +/** + * @file SimpleStreamBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include "SimpleStreamBuffer.h" + +static void try_output (SimpleStreamBuffer *o) +{ + ASSERT(o->output_data_len > 0) + + // calculate number of bytes to output + int bytes = bmin_int(o->output_data_len, o->buf_used); + if (bytes == 0) { + return; + } + + // copy bytes to output + memcpy(o->output_data, o->buf, bytes); + + // shift buffer + memmove(o->buf, o->buf + bytes, o->buf_used - bytes); + o->buf_used -= bytes; + + // forget data + o->output_data_len = -1; + + // done + StreamRecvInterface_Done(&o->output, bytes); +} + +static void output_handler_recv (SimpleStreamBuffer *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_data_len == -1) + ASSERT(data) + ASSERT(data_len > 0) + + // remember data + o->output_data = data; + o->output_data_len = data_len; + + try_output(o); +} + +int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg) +{ + ASSERT(buf_size > 0) + + // init arguments + o->buf_size = buf_size; + + // init output + StreamRecvInterface_Init(&o->output, (StreamRecvInterface_handler_recv)output_handler_recv, o, pg); + + // allocate buffer + if (!(o->buf = (uint8_t *)BAlloc(buf_size))) { + goto fail1; + } + + // init buffer state + o->buf_used = 0; + + // set no output data + o->output_data_len = -1; + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + StreamRecvInterface_Free(&o->output); + return 0; +} + +void SimpleStreamBuffer_Free (SimpleStreamBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + BFree(o->buf); + + // free output + StreamRecvInterface_Free(&o->output); +} + +StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= 0) + + if (data_len > o->buf_size - o->buf_used) { + return 0; + } + + // copy to buffer + memcpy(o->buf + o->buf_used, data, data_len); + + // update buffer state + o->buf_used += data_len; + + // continue outputting + if (o->output_data_len > 0) { + try_output(o); + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/client/SimpleStreamBuffer.h b/app/src/main/jni/badvpn/client/SimpleStreamBuffer.h new file mode 100644 index 0000000..31a55f7 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SimpleStreamBuffer.h @@ -0,0 +1,52 @@ +/** + * @file SimpleStreamBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SIMPLESTREAMBUFFER_H +#define BADVPN_SIMPLESTREAMBUFFER_H + +#include +#include +#include + +typedef struct { + int buf_size; + StreamRecvInterface output; + uint8_t *buf; + int buf_used; + uint8_t *output_data; + int output_data_len; + DebugObject d_obj; +} SimpleStreamBuffer; + +int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg) WARN_UNUSED; +void SimpleStreamBuffer_Free (SimpleStreamBuffer *o); +StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o); +int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/client/SinglePacketSource.c b/app/src/main/jni/badvpn/client/SinglePacketSource.c new file mode 100644 index 0000000..1c6a573 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SinglePacketSource.c @@ -0,0 +1,85 @@ +/** + * @file SinglePacketSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "SinglePacketSource.h" + +static void output_handler_recv (SinglePacketSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + // if we already sent one packet, stop + if (o->sent) { + return; + } + + // set sent + o->sent = 1; + + // write packet + memcpy(data, o->packet, o->packet_len); + + // done + PacketRecvInterface_Done(&o->output, o->packet_len); +} + +void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg) +{ + ASSERT(packet_len >= 0) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + + // set not sent + o->sent = 0; + + // init output + PacketRecvInterface_Init(&o->output, o->packet_len, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void SinglePacketSource_Free (SinglePacketSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/client/SinglePacketSource.h b/app/src/main/jni/badvpn/client/SinglePacketSource.h new file mode 100644 index 0000000..85ca426 --- /dev/null +++ b/app/src/main/jni/badvpn/client/SinglePacketSource.h @@ -0,0 +1,73 @@ +/** + * @file SinglePacketSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLEPACKETSOURCE_H +#define BADVPN_SINGLEPACKETSOURCE_H + +#include +#include + +/** + * An object which provides a single packet through {@link PacketRecvInterface}. + */ +typedef struct { + uint8_t *packet; + int packet_len; + int sent; + PacketRecvInterface output; + DebugObject d_obj; +} SinglePacketSource; + +/** + * Initializes the object. + * + * @param o the object + * @param packet packet to provide to the output. Must stay available until the packet is provided. + * @param packet_len length of packet. Must be >=0. + * @param pg pending group we live in + */ +void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SinglePacketSource_Free (SinglePacketSource *o); + +/** + * Returns the output interface. + * The MTU of the interface will be packet_len. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o); + +#endif diff --git a/app/src/main/jni/badvpn/client/StreamPeerIO.c b/app/src/main/jni/badvpn/client/StreamPeerIO.c new file mode 100644 index 0000000..3113c3b --- /dev/null +++ b/app/src/main/jni/badvpn/client/StreamPeerIO.c @@ -0,0 +1,712 @@ +/** + * @file StreamPeerIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#include + +#include + +#define MODE_NONE 0 +#define MODE_CONNECT 1 +#define MODE_LISTEN 2 + +#define CONNECT_STATE_CONNECTING 0 +#define CONNECT_STATE_HANDSHAKE 1 +#define CONNECT_STATE_SENDING 2 +#define CONNECT_STATE_SENT 3 +#define CONNECT_STATE_FINISHED 4 + +#define LISTEN_STATE_LISTENER 0 +#define LISTEN_STATE_GOTCLIENT 1 +#define LISTEN_STATE_FINISHED 2 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void decoder_handler_error (StreamPeerIO *pio); +static void connector_handler (StreamPeerIO *pio, int is_error); +static void connection_handler (StreamPeerIO *pio, int event); +static void connect_sslcon_handler (StreamPeerIO *pio, int event); +static void pwsender_handler (StreamPeerIO *pio); +static void listener_handler_client (StreamPeerIO *pio, sslsocket *sock); +static int init_io (StreamPeerIO *pio, sslsocket *sock); +static void free_io (StreamPeerIO *pio); +static void sslcon_handler (StreamPeerIO *pio, int event); +static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer); +static SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey); +static int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert); +static void reset_state (StreamPeerIO *pio); +static void reset_and_report_error (StreamPeerIO *pio); + +void decoder_handler_error (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + + PeerLog(pio, BLOG_ERROR, "decoder error"); + + reset_and_report_error(pio); + return; +} + +void connector_handler (StreamPeerIO *pio, int is_error) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_CONNECTING) + + // check connection result + if (is_error) { + PeerLog(pio, BLOG_NOTICE, "connection failed"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&pio->connect.sock.con, BConnection_source_connector(&pio->connect.connector), pio->reactor, pio, (BConnection_handler)connection_handler)) { + PeerLog(pio, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + if (pio->ssl) { + // init connection interfaces + BConnection_SendAsync_Init(&pio->connect.sock.con); + BConnection_RecvAsync_Init(&pio->connect.sock.con); + + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&pio->connect.sock.bottom_prfd, BConnection_SendAsync_GetIf(&pio->connect.sock.con), BConnection_RecvAsync_GetIf(&pio->connect.sock.con), pio->twd, pio->ssl_flags)) { + PeerLog(pio, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail1; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(pio->connect.sock.ssl_prfd = SSL_ImportFD(NULL, &pio->connect.sock.bottom_prfd))) { + ASSERT_FORCE(PR_Close(&pio->connect.sock.bottom_prfd) == PR_SUCCESS) + goto fail1; + } + + // set client mode + if (SSL_ResetHandshake(pio->connect.sock.ssl_prfd, PR_FALSE) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail2; + } + + // set verify peer certificate hook + if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, (SSLAuthCertificate)client_auth_certificate_callback, pio) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail2; + } + + // set client certificate callback + if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, (SSLGetClientAuthData)client_client_auth_data_callback, pio) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail2; + } + + // init BSSLConnection + BSSLConnection_Init(&pio->connect.sslcon, pio->connect.sock.ssl_prfd, 1, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)connect_sslcon_handler); + + // change state + pio->connect.state = CONNECT_STATE_HANDSHAKE; + } else { + // init connection send interface + BConnection_SendAsync_Init(&pio->connect.sock.con); + + // init password sender + SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BConnection_SendAsync_GetIf(&pio->connect.sock.con), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler); + + // change state + pio->connect.state = CONNECT_STATE_SENDING; + } + + return; + + if (pio->ssl) { +fail2: + ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS) +fail1: + BConnection_RecvAsync_Free(&pio->connect.sock.con); + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + BConnection_Free(&pio->connect.sock.con); +fail0: + reset_and_report_error(pio); + return; +} + +void connection_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN) + ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state >= CONNECT_STATE_HANDSHAKE) + ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state >= LISTEN_STATE_FINISHED) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + PeerLog(pio, BLOG_NOTICE, "connection closed"); + } else { + PeerLog(pio, BLOG_NOTICE, "connection error"); + } + + reset_and_report_error(pio); + return; +} + +void connect_sslcon_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) + ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR) + + if (event == BSSLCONNECTION_EVENT_ERROR) { + PeerLog(pio, BLOG_NOTICE, "SSL error"); + + reset_and_report_error(pio); + return; + } + + // handshake complete + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + + // remove client certificate callback + if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail0; + } + + // remove verify peer certificate callback + if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail0; + } + + // init password sender + SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BSSLConnection_GetSendIf(&pio->connect.sslcon), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler); + + // change state + pio->connect.state = CONNECT_STATE_SENDING; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +void pwsender_handler (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_SENDING) + + // stop using any buffers before they get freed + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->connect.sslcon); + } + + // free password sender + SingleStreamSender_Free(&pio->connect.pwsender); + + if (pio->ssl) { + // free BSSLConnection (we used the send interface) + BSSLConnection_Free(&pio->connect.sslcon); + } else { + // init connection send interface + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + + // change state + pio->connect.state = CONNECT_STATE_SENT; + + // setup i/o + if (!init_io(pio, &pio->connect.sock)) { + goto fail0; + } + + // change state + pio->connect.state = CONNECT_STATE_FINISHED; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +void listener_handler_client (StreamPeerIO *pio, sslsocket *sock) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_LISTEN) + ASSERT(pio->listen.state == LISTEN_STATE_LISTENER) + + // remember socket + pio->listen.sock = sock; + + // set connection handler + BConnection_SetHandlers(&pio->listen.sock->con, pio, (BConnection_handler)connection_handler); + + // change state + pio->listen.state = LISTEN_STATE_GOTCLIENT; + + // check ceritficate + if (pio->ssl) { + CERTCertificate *peer_cert = SSL_PeerCertificate(pio->listen.sock->ssl_prfd); + if (!peer_cert) { + PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed"); + goto fail0; + } + + // compare certificate to the one provided by the server + if (!compare_certificate(pio, peer_cert)) { + CERT_DestroyCertificate(peer_cert); + goto fail0; + } + + CERT_DestroyCertificate(peer_cert); + } + + // setup i/o + if (!init_io(pio, pio->listen.sock)) { + goto fail0; + } + + // change state + pio->listen.state = LISTEN_STATE_FINISHED; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +int init_io (StreamPeerIO *pio, sslsocket *sock) +{ + ASSERT(!pio->sock) + + // limit socket send buffer, else our scheduling is pointless + if (pio->sock_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&sock->con, pio->sock_sndbuf)) { + PeerLog(pio, BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + if (pio->ssl) { + // init BSSLConnection + BSSLConnection_Init(&pio->sslcon, sock->ssl_prfd, 0, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)sslcon_handler); + } else { + // init connection interfaces + BConnection_SendAsync_Init(&sock->con); + BConnection_RecvAsync_Init(&sock->con); + } + + StreamPassInterface *send_if = (pio->ssl ? BSSLConnection_GetSendIf(&pio->sslcon) : BConnection_SendAsync_GetIf(&sock->con)); + StreamRecvInterface *recv_if = (pio->ssl ? BSSLConnection_GetRecvIf(&pio->sslcon) : BConnection_RecvAsync_GetIf(&sock->con)); + + // init receiving + StreamRecvConnector_ConnectInput(&pio->input_connector, recv_if); + + // init sending + PacketStreamSender_Init(&pio->output_pss, send_if, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor)); + PacketPassConnector_ConnectOutput(&pio->output_connector, PacketStreamSender_GetInput(&pio->output_pss)); + + pio->sock = sock; + + return 1; +} + +void free_io (StreamPeerIO *pio) +{ + ASSERT(pio->sock) + + // stop using any buffers before they get freed + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->sslcon); + } + + // reset decoder + PacketProtoDecoder_Reset(&pio->input_decoder); + + // free sending + PacketPassConnector_DisconnectOutput(&pio->output_connector); + PacketStreamSender_Free(&pio->output_pss); + + // free receiving + StreamRecvConnector_DisconnectInput(&pio->input_connector); + + if (pio->ssl) { + // free BSSLConnection + BSSLConnection_Free(&pio->sslcon); + } else { + // free connection interfaces + BConnection_RecvAsync_Free(&pio->sock->con); + BConnection_SendAsync_Free(&pio->sock->con); + } + + pio->sock = NULL; +} + +void sslcon_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN) + ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state == CONNECT_STATE_FINISHED) + ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state == LISTEN_STATE_FINISHED) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + PeerLog(pio, BLOG_NOTICE, "SSL error"); + + reset_and_report_error(pio); + return; +} + +SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer) +{ + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + DebugObject_Access(&pio->d_obj); + + // This callback is used to bypass checking the server's domain name, as peers + // don't have domain names. We byte-compare the certificate to the one reported + // by the server anyway. + + SECStatus ret = SECFailure; + + CERTCertificate *server_cert = SSL_PeerCertificate(pio->connect.sock.ssl_prfd); + if (!server_cert) { + PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail1; + } + + if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), server_cert, PR_TRUE, certUsageSSLServer, SSL_RevealPinArg(pio->connect.sock.ssl_prfd)) != SECSuccess) { + goto fail2; + } + + // compare to certificate provided by the server + if (!compare_certificate(pio, server_cert)) { + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail2; + } + + ret = SECSuccess; + +fail2: + CERT_DestroyCertificate(server_cert); +fail1: + return ret; +} + +SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + DebugObject_Access(&pio->d_obj); + + CERTCertificate *cert = CERT_DupCertificate(pio->connect.ssl_cert); + if (!cert) { + PeerLog(pio, BLOG_ERROR, "CERT_DupCertificate failed"); + goto fail0; + } + + SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(pio->connect.ssl_key); + if (!key) { + PeerLog(pio, BLOG_ERROR, "SECKEY_CopyPrivateKey failed"); + goto fail1; + } + + *pRetCert = cert; + *pRetKey = key; + return SECSuccess; + +fail1: + CERT_DestroyCertificate(cert); +fail0: + return SECFailure; +} + +int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert) +{ + ASSERT(pio->ssl) + + SECItem der = cert->derCert; + if (der.len != pio->ssl_peer_cert_len || memcmp(der.data, pio->ssl_peer_cert, der.len)) { + PeerLog(pio, BLOG_NOTICE, "Client certificate doesn't match"); + return 0; + } + + return 1; +} + +void reset_state (StreamPeerIO *pio) +{ + // free resources + switch (pio->mode) { + case MODE_NONE: + break; + case MODE_LISTEN: + switch (pio->listen.state) { + case LISTEN_STATE_FINISHED: + free_io(pio); + case LISTEN_STATE_GOTCLIENT: + if (pio->ssl) { + ASSERT_FORCE(PR_Close(pio->listen.sock->ssl_prfd) == PR_SUCCESS) + BConnection_RecvAsync_Free(&pio->listen.sock->con); + BConnection_SendAsync_Free(&pio->listen.sock->con); + } + BConnection_Free(&pio->listen.sock->con); + free(pio->listen.sock); + case LISTEN_STATE_LISTENER: + if (pio->listen.state == LISTEN_STATE_LISTENER) { + PasswordListener_RemoveEntry(pio->listen.listener, &pio->listen.pwentry); + } + break; + default: + ASSERT(0); + } + break; + case MODE_CONNECT: + switch (pio->connect.state) { + case CONNECT_STATE_FINISHED: + free_io(pio); + case CONNECT_STATE_SENT: + case CONNECT_STATE_SENDING: + if (pio->connect.state == CONNECT_STATE_SENDING) { + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->connect.sslcon); + } + SingleStreamSender_Free(&pio->connect.pwsender); + if (!pio->ssl) { + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + } + case CONNECT_STATE_HANDSHAKE: + if (pio->ssl) { + if (pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) { + BSSLConnection_Free(&pio->connect.sslcon); + } + ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS) + BConnection_RecvAsync_Free(&pio->connect.sock.con); + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + BConnection_Free(&pio->connect.sock.con); + case CONNECT_STATE_CONNECTING: + BConnector_Free(&pio->connect.connector); + break; + default: + ASSERT(0); + } + break; + default: + ASSERT(0); + } + + // set mode none + pio->mode = MODE_NONE; + + ASSERT(!pio->sock) +} + +void reset_and_report_error (StreamPeerIO *pio) +{ + reset_state(pio); + + pio->handler_error(pio->user); + return; +} + +int StreamPeerIO_Init ( + StreamPeerIO *pio, + BReactor *reactor, + BThreadWorkDispatcher *twd, + int ssl, + int ssl_flags, + uint8_t *ssl_peer_cert, + int ssl_peer_cert_len, + int payload_mtu, + int sock_sndbuf, + PacketPassInterface *user_recv_if, + BLog_logfunc logfunc, + StreamPeerIO_handler_error handler_error, + void *user +) +{ + ASSERT(ssl == 0 || ssl == 1) + ASSERT(payload_mtu >= 0) + ASSERT(PacketPassInterface_GetMTU(user_recv_if) >= payload_mtu) + ASSERT(handler_error) + + // init arguments + pio->reactor = reactor; + pio->twd = twd; + pio->ssl = ssl; + if (pio->ssl) { + pio->ssl_flags = ssl_flags; + pio->ssl_peer_cert = ssl_peer_cert; + pio->ssl_peer_cert_len = ssl_peer_cert_len; + } + pio->payload_mtu = payload_mtu; + pio->sock_sndbuf = sock_sndbuf; + pio->logfunc = logfunc; + pio->handler_error = handler_error; + pio->user = user; + + // check payload MTU + if (pio->payload_mtu > PACKETPROTO_MAXPAYLOAD) { + PeerLog(pio, BLOG_ERROR, "payload MTU is too large"); + goto fail0; + } + + // init receiveing objects + StreamRecvConnector_Init(&pio->input_connector, BReactor_PendingGroup(pio->reactor)); + if (!PacketProtoDecoder_Init(&pio->input_decoder, StreamRecvConnector_GetOutput(&pio->input_connector), user_recv_if, BReactor_PendingGroup(pio->reactor), pio, + (PacketProtoDecoder_handler_error)decoder_handler_error + )) { + PeerLog(pio, BLOG_ERROR, "FlowErrorDomain_Init failed"); + goto fail1; + } + + // init sending objects + PacketCopier_Init(&pio->output_user_copier, pio->payload_mtu, BReactor_PendingGroup(pio->reactor)); + PacketProtoEncoder_Init(&pio->output_user_ppe, PacketCopier_GetOutput(&pio->output_user_copier), BReactor_PendingGroup(pio->reactor)); + PacketPassConnector_Init(&pio->output_connector, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor)); + if (!SinglePacketBuffer_Init(&pio->output_user_spb, PacketProtoEncoder_GetOutput(&pio->output_user_ppe), PacketPassConnector_GetInput(&pio->output_connector), BReactor_PendingGroup(pio->reactor))) { + PeerLog(pio, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // set mode none + pio->mode = MODE_NONE; + + // set no socket + pio->sock = NULL; + + DebugObject_Init(&pio->d_obj); + return 1; + +fail2: + PacketPassConnector_Free(&pio->output_connector); + PacketProtoEncoder_Free(&pio->output_user_ppe); + PacketCopier_Free(&pio->output_user_copier); + PacketProtoDecoder_Free(&pio->input_decoder); +fail1: + StreamRecvConnector_Free(&pio->input_connector); +fail0: + return 0; +} + +void StreamPeerIO_Free (StreamPeerIO *pio) +{ + DebugObject_Free(&pio->d_obj); + + // reset state + reset_state(pio); + + // free sending objects + SinglePacketBuffer_Free(&pio->output_user_spb); + PacketPassConnector_Free(&pio->output_connector); + PacketProtoEncoder_Free(&pio->output_user_ppe); + PacketCopier_Free(&pio->output_user_copier); + + // free receiveing objects + PacketProtoDecoder_Free(&pio->input_decoder); + StreamRecvConnector_Free(&pio->input_connector); +} + +PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + + return PacketCopier_GetInput(&pio->output_user_copier); +} + +int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key) +{ + DebugObject_Access(&pio->d_obj); + + // reset state + reset_state(pio); + + // check address + if (!BConnection_AddressSupported(addr)) { + PeerLog(pio, BLOG_ERROR, "BConnection_AddressSupported failed"); + goto fail0; + } + + // init connector + if (!BConnector_Init(&pio->connect.connector, addr, pio->reactor, pio, (BConnector_handler)connector_handler)) { + PeerLog(pio, BLOG_ERROR, "BConnector_Init failed"); + goto fail0; + } + + // remember data + if (pio->ssl) { + pio->connect.ssl_cert = ssl_cert; + pio->connect.ssl_key = ssl_key; + } + pio->connect.password = htol64(password); + + // set state + pio->mode = MODE_CONNECT; + pio->connect.state = CONNECT_STATE_CONNECTING; + + return 1; + +fail0: + return 0; +} + +void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(listener->ssl == pio->ssl) + + // reset state + reset_state(pio); + + // add PasswordListener entry + uint64_t newpass = PasswordListener_AddEntry(listener, &pio->listen.pwentry, (PasswordListener_handler_client)listener_handler_client, pio); + + // remember data + pio->listen.listener = listener; + + // set state + pio->mode = MODE_LISTEN; + pio->listen.state = LISTEN_STATE_LISTENER; + + *password = newpass; +} diff --git a/app/src/main/jni/badvpn/client/StreamPeerIO.h b/app/src/main/jni/badvpn/client/StreamPeerIO.h new file mode 100644 index 0000000..0b6b260 --- /dev/null +++ b/app/src/main/jni/badvpn/client/StreamPeerIO.h @@ -0,0 +1,222 @@ +/** + * @file StreamPeerIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used for communicating with a peer over TCP. + */ + +#ifndef BADVPN_CLIENT_STREAMPEERIO_H +#define BADVPN_CLIENT_STREAMPEERIO_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Callback function invoked when an error occurs with the peer connection. + * The object has entered default state. + * May be called from within a sending Send call. + * + * @param user value given to {@link StreamPeerIO_Init}. + */ +typedef void (*StreamPeerIO_handler_error) (void *user); + +/** + * Object used for communicating with a peer over TCP. + * The object has a logical state which can be one of the following: + * - default state + * - listening state + * - connecting state + */ +typedef struct { + // common arguments + BReactor *reactor; + BThreadWorkDispatcher *twd; + int ssl; + int ssl_flags; + uint8_t *ssl_peer_cert; + int ssl_peer_cert_len; + int payload_mtu; + int sock_sndbuf; + BLog_logfunc logfunc; + StreamPeerIO_handler_error handler_error; + void *user; + + // persistent I/O modules + + // base sending objects + PacketCopier output_user_copier; + PacketProtoEncoder output_user_ppe; + SinglePacketBuffer output_user_spb; + PacketPassConnector output_connector; + + // receiving objects + StreamRecvConnector input_connector; + PacketProtoDecoder input_decoder; + + // connection side + int mode; + + union { + // listening data + struct { + int state; + PasswordListener *listener; + PasswordListener_pwentry pwentry; + sslsocket *sock; + } listen; + // connecting data + struct { + int state; + CERTCertificate *ssl_cert; + SECKEYPrivateKey *ssl_key; + BConnector connector; + sslsocket sock; + BSSLConnection sslcon; + uint64_t password; + SingleStreamSender pwsender; + } connect; + }; + + // socket data + sslsocket *sock; + BSSLConnection sslcon; + + // sending objects + PacketStreamSender output_pss; + + DebugObject d_obj; +} StreamPeerIO; + +/** + * Initializes the object. + * The object is initialized in default state. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSSLConnection_GlobalInit} must have been done if using SSL. + * + * @param pio the object + * @param reactor reactor we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param ssl if nonzero, SSL will be used for peer connection + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param ssl_peer_cert if using SSL, the certificate we expect the peer to have + * @param ssl_peer_cert_len if using SSL, the length of the certificate + * @param payload_mtu maximum packet size as seen from the user. Must be >=0. + * @param sock_sndbuf socket SO_SNDBUF option. Specify <=0 to not set it. + * @param user_recv_if interface to use for submitting received packets. Its MTU + * must be >=payload_mtu. + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @param handler_error handler function invoked when a connection error occurs + * @param user value to pass to handler functions + * @return 1 on success, 0 on failure + */ +int StreamPeerIO_Init ( + StreamPeerIO *pio, + BReactor *reactor, + BThreadWorkDispatcher *twd, + int ssl, + int ssl_flags, + uint8_t *ssl_peer_cert, + int ssl_peer_cert_len, + int payload_mtu, + int sock_sndbuf, + PacketPassInterface *user_recv_if, + BLog_logfunc logfunc, + StreamPeerIO_handler_error handler_error, + void *user +) WARN_UNUSED; + +/** + * Frees the object. + * + * @param pio the object + */ +void StreamPeerIO_Free (StreamPeerIO *pio); + +/** + * Returns the interface for sending packets to the peer. + * The OTP warning handler may be called from within Send calls + * to the interface. + * + * @param pio the object + * @return interface for sending packets to the peer + */ +PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio); + +/** + * Starts an attempt to connect to the peer. + * On success, the object enters connecting state. + * On failure, the object enters default state. + * + * @param pio the object + * @param addr address to connect to + * @param password identification code to send to the peer + * @param ssl_cert if using SSL, the client certificate to use. This object does not + * take ownership of the certificate; it must remain valid until + * the object is reset. + * @param ssl_key if using SSL, the private key to use. This object does not take + * ownership of the key; it must remain valid until the object is reset. + * @return 1 on success, 0 on failure + */ +int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key) WARN_UNUSED; + +/** + * Starts an attempt to accept a connection from the peer. + * The object enters listening state. + * + * @param pio the object + * @param listener {@link PasswordListener} object to use for accepting a connection. + * The listener must have SSL enabled if and only if this object has + * SSL enabled. The listener must be available until the object is + * reset or {@link StreamPeerIO_handler_up} is called. + * @param password will return the identification code the peer should send when connecting + */ +void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password); + +#endif diff --git a/app/src/main/jni/badvpn/client/badvpn-client.8 b/app/src/main/jni/badvpn/client/badvpn-client.8 new file mode 100644 index 0000000..4f41203 --- /dev/null +++ b/app/src/main/jni/badvpn/client/badvpn-client.8 @@ -0,0 +1,316 @@ +.TH badvpn-client 8 "14 July 2011" +.SH NAME +badvpn-client \- VPN node daemon for the BadVPN peer-to-peer VPN system +.SH SYNOPSIS +.B badvpn-client +.RS +.RB "[" --help "]" +.br +.RB "[" --version "]" +.br +.RB "[" --logger " ]" +.br +(logger=syslog? +.br +.RS +.br +.RB "[" --syslog-facility " ]" +.br +.RB "[" --syslog-ident " ]" +.br +.RE +) +.br +.RB "[" --loglevel " <0-5/none/error/warning/notice/info/debug>]" +.br +.RB "[" --channel-loglevel " <0-5/none/error/warning/notice/info/debug>] ..." +.br +.RB "[" --threads " ]" +.br +.RB "[" --ssl " " --nssdb " " --client-cert-name " ]" +.br +.RB "[" --server-name " ]" +.br +.BR --server-addr " " +.br +.RB "[" --tapdev " ]" +.br +.RB "[" --scope " ] ..." +.br +[ +.br +.RS +.BR --bind-addr " " +.br +.RB "(transport-mode=udp? " --num-ports " )" +.br +.RB "[" --ext-addr " ] ..." +.br +.RE +] ... +.br +.BR --transport-mode " " +.br +(transport-mode=udp? +.br +.RS +.BR --encryption-mode " " +.br +.BR --hash-mode " " +.br +.RB "[" --otp " ]" +.br +.RB "[" --fragmentation-latency " ]" +.br +.RE +) +.br +(transport-mode=tcp? +.br +.RS +.RB "(ssl? [" --peer-ssl "])" +.br +.RB "[" --peer-tcp-socket-sndbuf " ]" +.br +.RE +) +.br +.RB "[" --send-buffer-size " ]" +.br +.RB "[" --send-buffer-relay-size " ]" +.br +.RB "[" --max-macs " ]" +.br +.RB "[" --max-groups " ]" +.br +.RB "[" --igmp-group-membership-interval " ]" +.br +.RB "[" --igmp-last-member-query-time " ]" +.br +.RB "[" --allow-peer-talk-without-ssl "]" +.br +.RE +.SH INTRODUCTION +.P +This page documents the BadVPN client, a daemon for a node in a BadVPN VPN network. +For a general description of BadVPN, see +.BR badvpn (7). +.SH DESCRIPTION +.P +The BadVPN client is a daemon that runs on a VPN node. It opens the TAP device, connects to +the server, then keeps running while attempting to establish data connection to peers and +tranferring data between the TAP device and the peers. Once it initializes, the program only +terminates if it loses connection to the server, or if a signal is received. +.SH OPTIONS +.P +The BadVPN client is configured entirely from command line. +.TP +.BR --help +Print version and command line syntax and exit. +.TP +.BR --version +Print version and exit. +.TP +.BR --logger " " +Select where to log messages. Default is stdout. Syslog is not available on Windows. +.TP +.BR --syslog-facility " " +When logging to syslog, set the logging facility. The facility name must be in lower case. +.TP +.BR --syslog-ident " " +When logging to syslog, set the ident. +.TP +.BR --loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the default logging level. +.TP +.BR --channel-loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the logging level for a specific logging channel. +.TP +.BR --threads " " +Hint for the number of additional threads to use for potentionally long computations (such as +encryption and OTP generation). If zero (0) (default), additional threads will be disabled and all +computations will be done in the event loop. If negative (<0), a guess will be made, possibly +based on the number of CPUs. If positive (>0), the given number of threads will be used. +.TP +.BR --ssl +Use TLS. Requires --nssdb and --server-cert-name. +.TP +.BR --nssdb " " +When using TLS, the NSS database to use. Probably something like sql:/some/folder. +.TP +.BR --client-cert-name " " +When using TLS, the name of the certificate to use. The certificate must be readily accessible. +.TP +.BR --server-name " " +Set the name of the server used for validating the server's certificate. The server name defaults +to the the name in the server address (or a numeric address). +.TP +.BR --server-addr " " +Set the address for the server to listen on. See below for address format. +.TP +.BR --tapdev " " +Set the TAP device to use. See below on how to configure the device. A TAP device is a virtual card +in the operating system, but rather than receiving from and sending frames to a piece of hardware, +a program (this one) opens it to read from and write frames into. If the VPN network is set up correctly, +the TAP devices on the VPN nodes will act as if they were all connected into a network switch. +.TP +.BR --scope " " +Add an address scope allowed for connecting to peers. May be specified multiple times to add multiple +scopes. The order of the scopes is irrelevant. Note that it must actually be possible to connect +to addresses in the given scope; when another peer binds for us to connect to, we choose the first +external address whose scope we recognize, and do not attempt further external addresses, even if +establishing the connection fails. +.TP +.BR --bind-addr " " +Add an address to allow binding on. See below for address format. When attempting to bind in order +for some peer to connect to us, the addresses will be tried in the order they are specified. If UDP +data transport is being used, a --num-ports option must follow to specify how many continuous ports +to allow binding to. For the address to be useful, one or more --ext-addr options must follow. +Note that when two peers need to establish a data connection, it is arbitrary which one will attempt +to bind first. +.TP +.BR --num-ports " " +When using UDP transport, set the number of continuous ports for a previously specified bind address. +Must follow a previous --bind-addr option. +.TP +.BR --ext-addr " " +Add an external address for a previously specified bind address. Must follow a previous --bind-addr +option. May be specified multiple times to add multiple external addresses. See below for address +format. Additionally, the IP address part can be {server_reported} to use the IPv4 address as the +server sees us. The external addresses are tried by the connecting peer in the order they are specified. +Note that the connecting peer only attempts to connect to the first address whose scope it recognizes +and does not try other addresses. This means that all addresses must work for be able to communicate. +.TP +.BR --transport-mode " " +Sets the transport protocol for data connections. UDP is recommended and works best for most networks. +TCP can be used instead if the underlying network has high packet loss which your virtual network +cannot tolerate. Must match on all peers. +.TP +.BR --encryption-mode " " +When using UDP transport, sets the encryption mode. None means no encryption, other options mean +a specific cipher. Note that encryption is only useful if clients use TLS to connect to the server. +The encryption mode must match on all peers. +.TP +.BR --hash-mode " " +When using UDP transport, sets the hashing mode. None means no hashes, other options mean a specific +type of hash. Note that hashing is only useful if encryption is used as well. The hash mode must +match on all peers. +.TP +.BR --otp " " +When using UDP transport, enables one-time passwords. The first argument specifies a block cipher +used to generate passwords from a seed. The second argument specifies how many passwords are +generated from a single seed. The third argument specifies after how many passwords used up for +sending packets an attempt is made to negotiate a new seed with the other peer. num must be >0, +and num-warn must be >0 and <=num. The difference (num - num-warn) should be large enough to allow +a new seed to be negotiated before the sender runs out of passwords. Negotiating a seed involves +the sending peer sending it to the receiving peer via the server and the receiving peer confirming +it via the server. Note that one-time passwords are only useful if clients use TLS to connect to the +server. The OTP option must match on all peers, except for num-warn. +.TP +.BR --fragmentation-latency " " +When using UDP transport, sets the maximum latency to sacrifice in order to pack frames into data +packets more efficiently. If it is >=0, a timer of that many milliseconds is used to wait for further +frames to put into an incomplete packet since the first chunk of the packet was written. If it is +<0, packets are sent out immediately. Defaults to 0, which is the recommended setting. +.TP +.BR --peer-ssl +When using TCP transport, enables TLS for data connections. Requires using TLS for server connection. +For this to work, the peers must trust each others' cerificates, and the cerificates must grant the +TLS server usage context. This option must match on all peers. +.TP +.BR --peer-tcp-socket-sndbuf " " +Sets the value of the SO_SNDBUF socket option for peer TCP sockets (zero to not set). Lower values +will improve fairness when data from multiple sources (local and relaying) is being sent to a +given peer, but may result in lower bandwidth if the network's bandwidth-delay product is too big. +.TP +.BR --send-buffer-size " " +Sets the minimum size of the peers' send buffers for sending frames originating from this system, in +number of packets. +.TP +.BR --send-buffer-relay-size " " +Sets the minimum size of the peers' send buffers for relaying frames from other peers, in number of +packets. +.TP +.BR --max-macs " " +Sets the maximum number of MAC addresses to remember for a peer. When the number is exceeded, the least +recently used slot will be reused. +.TP +.BR --max-groups " " +Sets the maximum number of IGMP group memberships to remember for a peer. When the number is exceeded, +the least recently used slot will be reused. +.TP +.BR --igmp-group-membership-interval " " +Sets the Group Membership Interval parameter for IGMP snooping, in milliseconds. +.TP +.BR --igmp-last-member-query-time " " +Sets the Last Member Query Time parameter for IGMP snooping, in milliseconds. +.TP +.BR --allow-peer-talk-without-ssl +When SSL is enabled, the clients not only connect to the server using SSL, but also exchange messages through +the server through another layer of SSL. This protects the messages from attacks on the server. Older versions +of BadVPN (<1.999.109), however, do not support this. This option allows older and newer clients to +interoperate by not using SSL if the other peer does not support it. It does however negate the security +benefits of using SSL, since the (potentionally compromised) server can then order peers not to use SSL. +.SH "EXIT CODE" +.P +If initialization fails, exits with code 1. Otherwise runs until termination is requested or server connection +is broken and exits with code 1. +.SH "ADDRESS FORMAT" +.P +Addresses have the form ipaddr:port, where ipaddr is either an IPv4 address (name or numeric), or an +IPv6 address enclosed in brackets [] (name or numeric again). +.SH "TAP DEVICE CONFIGURATION" +.P +To use this program, you first have to configure a TAP network device that will act as an endpoint for +the virtual network. The configuration depends on your operating system. +.P +Note that the client program does not configure the TAP device in any way; it only reads and writes +frames from/to it. You are responsible for configuring it (e.g. putting it up and setting its IP address). +.P +.B Linux +.P +You need to enable the kernel configuration option CONFIG_TUN. If you enabled it as a module, you may +have to load it (`modprobe tun`) before you can create the device. +.P +Then you should create a persistent TAP device for the VPN client program to open. This can be done with +either the +.B tunctl +or the +.B openvpn +program. The device will be associated with a user account that will have permission to use it, which should +be the same user as the client program will run as (not root!). To create the device with tunctl, use `tunctl -u -t tapN`, +and to create it with openvpn, use `openvpn --mktun --user --dev tapN`, where N is a number that identifies the +TAP device. +.P +Once the TAP device is created, pass `--tapdev tapN` to the client program to make it use this device. Note that the +device will not be preserved across a shutdown of the system; consult your OS documentaton if you want to automate +the creation or configuration of the device. +.P +.B Windows +.P +Windows does not come with a TAP driver. The client program uses the TAP-Win32 driver, which is part of OpenVPN. +You need to install the OpenVPN open source (!) version, and in the installer enable at least the +`TAP Virtual Ethernet Adapter` and `Add Shortcuts to Start Menu` options. +You can get the installer at +.br +. +.P +The OpenVPN installer automatically creates one TAP device on your system when it's run for the first time. +To create another device, use `Programs -> OpenVPN -> Utilities -> Add a new TAP virtual ethernet adapter`. +You may have to install OpenVPN once again to make this shortcut appear. +.P +Once you have a TAP device, you can configure it like a physical network card. You can recognize TAP devices +by their `Device Name` field. +.P +To use the device, pass `--tapdev ":"` to the client program, where is the name of +the TAP driver (tap0901 for OpenVPN 2.1 and 2.2) (case sensitive), and is the (human) name of the TAP +network interface (e.g. `Local Area Connection 2`). +.SH "EXAMPLES" +.P +For examples of using BadVPN, see +.BR badvpn (7). +.SH "SEE ALSO" +.BR badvpn-server (8), +.BR badvpn (7) +.SH AUTHORS +Ambroz Bizjak diff --git a/app/src/main/jni/badvpn/client/client.c b/app/src/main/jni/badvpn/client/client.c new file mode 100644 index 0000000..2729d6b --- /dev/null +++ b/app/src/main/jni/badvpn/client/client.c @@ -0,0 +1,2997 @@ +/** + * @file client.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define TRANSPORT_MODE_UDP 0 +#define TRANSPORT_MODE_TCP 1 + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// command-line options +struct ext_addr_option { + char *addr; + char *scope; +}; +struct bind_addr_option { + char *addr; + int num_ports; + int num_ext_addrs; + struct ext_addr_option ext_addrs[MAX_EXT_ADDRS]; +}; +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int threads; + int use_threads_for_ssl_handshake; + int use_threads_for_ssl_data; + int ssl; + char *nssdb; + char *client_cert_name; + char *server_name; + char *server_addr; + char *tapdev; + int num_scopes; + char *scopes[MAX_SCOPES]; + int num_bind_addrs; + struct bind_addr_option bind_addrs[MAX_BIND_ADDRS]; + int transport_mode; + int encryption_mode; + int hash_mode; + int otp_mode; + int otp_num; + int otp_num_warn; + int fragmentation_latency; + int peer_ssl; + int peer_tcp_socket_sndbuf; + int send_buffer_size; + int send_buffer_relay_size; + int max_macs; + int max_groups; + int igmp_group_membership_interval; + int igmp_last_member_query_time; + int allow_peer_talk_without_ssl; + int max_peers; +} options; + +// bind addresses +struct ext_addr { + int server_reported_port; + BAddr addr; // if server_reported_port>=0, defined only after hello received + char scope[64]; +}; +struct bind_addr { + BAddr addr; + int num_ports; + int num_ext_addrs; + struct ext_addr ext_addrs[MAX_EXT_ADDRS]; +}; +int num_bind_addrs; +struct bind_addr bind_addrs[MAX_BIND_ADDRS]; + +// TCP listeners +PasswordListener listeners[MAX_BIND_ADDRS]; + +// SPProto parameters (UDP only) +struct spproto_security_params sp_params; + +// server address we connect to +BAddr server_addr; + +// server name to use for SSL +char server_name[256]; + +// reactor +BReactor ss; + +// thread work dispatcher +BThreadWorkDispatcher twd; + +// client certificate if using SSL +CERTCertificate *client_cert; + +// client private key if using SSL +SECKEYPrivateKey *client_key; + +// device +BTap device; +int device_mtu; + +// DataProtoSource for device input (reading) +DataProtoSource device_dpsource; + +// DPReceiveDevice for device output (writing) +DPReceiveDevice device_output_dprd; + +// data communication MTU +int data_mtu; + +// peers list +LinkedList1 peers; +int num_peers; + +// frame decider +FrameDecider frame_decider; + +// peers that can be user as relays +LinkedList1 relays; + +// peers than need a relay +LinkedList1 waiting_relay_peers; + +// server connection +ServerConnection server; + +// my ID, defined only after server_ready +peerid_t my_id; + +// fair queue for sending peer messages to the server +PacketPassFairQueue server_queue; + +// whether server is ready +int server_ready; + +// dying server flow +struct server_flow *dying_server_flow; + +// stops event processing, causing the program to exit +static void terminate (void); + +// prints program name and version to standard output +static void print_help (const char *name); + +// prints program name and version to standard output +static void print_version (void); + +// parses the command line +static int parse_arguments (int argc, char *argv[]); + +// processes certain command line options +static int process_arguments (void); + +static int ssl_flags (void); + +// handler for program termination request +static void signal_handler (void *unused); + +// adds a new peer +static void peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len); + +// removes a peer +static void peer_remove (struct peer_data *peer, int exiting); + +// appends the peer log prefix to the logger +static void peer_logfunc (struct peer_data *peer); + +// passes a message to the logger, prepending it info about the peer +static void peer_log (struct peer_data *peer, int level, const char *fmt, ...); + +// see if we are the master relative to this peer +static int peer_am_master (struct peer_data *peer); + +// frees PeerChat, disconnecting it from the server flow +static void peer_free_chat (struct peer_data *peer); + +// initializes the link +static int peer_init_link (struct peer_data *peer); + +// frees link resources +static void peer_free_link (struct peer_data *peer); + +// frees link, relaying, waiting relaying +static void peer_cleanup_connections (struct peer_data *peer); + +// registers the peer as a relay provider +static void peer_enable_relay_provider (struct peer_data *peer); + +// unregisters the peer as a relay provider +static void peer_disable_relay_provider (struct peer_data *peer); + +// install relaying for a peer +static void peer_install_relaying (struct peer_data *peer, struct peer_data *relay); + +// uninstall relaying for a peer +static void peer_free_relaying (struct peer_data *peer); + +// handle a peer that needs a relay +static void peer_need_relay (struct peer_data *peer); + +// inserts the peer into the need relay list +static void peer_register_need_relay (struct peer_data *peer); + +// removes the peer from the need relay list +static void peer_unregister_need_relay (struct peer_data *peer); + +// handle a link setup failure +static void peer_reset (struct peer_data *peer); + +// fees chat and sends resetpeer +static void peer_resetpeer (struct peer_data *peer); + +// chat handlers +static void peer_chat_handler_error (struct peer_data *peer); +static void peer_chat_handler_message (struct peer_data *peer, uint8_t *data, int data_len); + +// handlers for different message types +static void peer_msg_youconnect (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_cannotconnect (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_cannotbind (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_seed (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_confirmseed (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_youretry (struct peer_data *peer, uint8_t *data, int data_len); + +// handler from DatagramPeerIO when we should generate a new OTP send seed +static void peer_udp_pio_handler_seed_warning (struct peer_data *peer); + +// handler from DatagramPeerIO when a new OTP seed can be recognized once it was provided to it +static void peer_udp_pio_handler_seed_ready (struct peer_data *peer); + +// handler from DatagramPeerIO when an error occurs on the connection +static void peer_udp_pio_handler_error (struct peer_data *peer); + +// handler from StreamPeerIO when an error occurs on the connection +static void peer_tcp_pio_handler_error (struct peer_data *peer); + +// peer retry timer handler. The timer is used only on the master side, +// wither when we detect an error, or the peer reports an error. +static void peer_reset_timer_handler (struct peer_data *peer); + +// start binding, according to the protocol +static void peer_start_binding (struct peer_data *peer); + +// tries binding on one address, according to the protocol +static void peer_bind (struct peer_data *peer); + +static void peer_bind_one_address (struct peer_data *peer, int addr_index, int *cont); + +static void peer_connect (struct peer_data *peer, BAddr addr, uint8_t *encryption_key, uint64_t password); + +static int peer_start_msg (struct peer_data *peer, void **data, int type, int len); + +static void peer_end_msg (struct peer_data *peer); + +// sends a message with no payload to the peer +static void peer_send_simple (struct peer_data *peer, int msgid); + +static void peer_send_conectinfo (struct peer_data *peer, int addr_index, int port_adjust, uint8_t *enckey, uint64_t pass); + +static void peer_send_confirmseed (struct peer_data *peer, uint16_t seed_id); + +// handler for peer DataProto up state changes +static void peer_dataproto_handler (struct peer_data *peer, int up); + +// looks for a peer with the given ID +static struct peer_data * find_peer_by_id (peerid_t id); + +// device error handler +static void device_error_handler (void *unused); + +// DataProtoSource handler for packets from the device +static void device_dpsource_handler (void *unused, const uint8_t *frame, int frame_len); + +// assign relays to clients waiting for them +static void assign_relays (void); + +// checks if the given address scope is known (i.e. we can connect to an address in it) +static char * address_scope_known (uint8_t *name, int name_len); + +// handlers for server messages +static void server_handler_error (void *user); +static void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip); +static void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); +static void server_handler_endclient (void *user, peerid_t peer_id); +static void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +// jobs +static void peer_job_send_seed (struct peer_data *peer); +static void peer_job_init (struct peer_data *peer); + +// server flows +static struct server_flow * server_flow_init (void); +static void server_flow_free (struct server_flow *flow); +static void server_flow_die (struct server_flow *flow); +static void server_flow_qflow_handler_busy (struct server_flow *flow); +static void server_flow_connect (struct server_flow *flow, PacketRecvInterface *input); +static void server_flow_disconnect (struct server_flow *flow); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + if (options.ssl) { + // init NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // register local NSPR file types + if (!DummyPRFileDesc_GlobalInit()) { + BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed"); + goto fail01; + } + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail01; + } + + // init NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail01; + } + + // set cipher policy + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // init server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.client_cert_name, &client_cert, &client_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail03; + } + } + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // init thread work dispatcher + if (!BThreadWorkDispatcher_Init(&twd, &ss, options.threads)) { + BLog(BLOG_ERROR, "BThreadWorkDispatcher_Init failed"); + goto fail3; + } + + // init BSecurity + if (BThreadWorkDispatcher_UsingThreads(&twd)) { + if (!BSecurity_GlobalInitThreadSafe()) { + BLog(BLOG_ERROR, "BSecurity_GlobalInitThreadSafe failed"); + goto fail4; + } + } + + // init listeners + int num_listeners = 0; + if (options.transport_mode == TRANSPORT_MODE_TCP) { + while (num_listeners < num_bind_addrs) { + struct bind_addr *addr = &bind_addrs[num_listeners]; + if (!PasswordListener_Init(&listeners[num_listeners], &ss, &twd, addr->addr, TCP_MAX_PASSWORD_LISTENER_CLIENTS, options.peer_ssl, ssl_flags(), client_cert, client_key)) { + BLog(BLOG_ERROR, "PasswordListener_Init failed"); + goto fail8; + } + num_listeners++; + } + } + + // init device + if (!BTap_Init(&device, &ss, options.tapdev, device_error_handler, NULL, 0)) { + BLog(BLOG_ERROR, "BTap_Init failed"); + goto fail8; + } + + // remember device MTU + device_mtu = BTap_GetMTU(&device); + + BLog(BLOG_INFO, "device MTU is %d", device_mtu); + + // calculate data MTU + if (device_mtu > INT_MAX - DATAPROTO_MAX_OVERHEAD) { + BLog(BLOG_ERROR, "Device MTU is too large"); + goto fail9; + } + data_mtu = DATAPROTO_MAX_OVERHEAD + device_mtu; + + // init device input + if (!DataProtoSource_Init(&device_dpsource, BTap_GetOutput(&device), device_dpsource_handler, NULL, &ss)) { + BLog(BLOG_ERROR, "DataProtoSource_Init failed"); + goto fail9; + } + + // init device output + if (!DPReceiveDevice_Init(&device_output_dprd, device_mtu, (DPReceiveDevice_output_func)BTap_Send, &device, &ss, options.send_buffer_relay_size, PEER_RELAY_FLOW_INACTIVITY_TIME)) { + BLog(BLOG_ERROR, "DPReceiveDevice_Init failed"); + goto fail10; + } + + // init peers list + LinkedList1_Init(&peers); + num_peers = 0; + + // init frame decider + FrameDecider_Init(&frame_decider, options.max_macs, options.max_groups, options.igmp_group_membership_interval, options.igmp_last_member_query_time, &ss); + + // init relays list + LinkedList1_Init(&relays); + + // init need relay list + LinkedList1_Init(&waiting_relay_peers); + + // start connecting to server + if (!ServerConnection_Init(&server, &ss, &twd, server_addr, SC_KEEPALIVE_INTERVAL, SERVER_BUFFER_MIN_PACKETS, options.ssl, ssl_flags(), client_cert, client_key, server_name, NULL, + server_handler_error, server_handler_ready, server_handler_newclient, server_handler_endclient, server_handler_message + )) { + BLog(BLOG_ERROR, "ServerConnection_Init failed"); + goto fail11; + } + + // set server not ready + server_ready = 0; + + // set no dying flow + dying_server_flow = NULL; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + if (server_ready) { + // allow freeing server queue flows + PacketPassFairQueue_PrepareFree(&server_queue); + + // make ServerConnection stop using buffers from peers before they are freed + ServerConnection_ReleaseBuffers(&server); + } + + // free peers + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&peers)) { + struct peer_data *peer = UPPER_OBJECT(node, struct peer_data, list_node); + peer_remove(peer, 1); + } + + // free dying server flow + if (dying_server_flow) { + server_flow_free(dying_server_flow); + } + + if (server_ready) { + PacketPassFairQueue_Free(&server_queue); + } + ServerConnection_Free(&server); +fail11: + FrameDecider_Free(&frame_decider); + DPReceiveDevice_Free(&device_output_dprd); +fail10: + DataProtoSource_Free(&device_dpsource); +fail9: + BTap_Free(&device); +fail8: + if (options.transport_mode == TRANSPORT_MODE_TCP) { + while (num_listeners-- > 0) { + PasswordListener_Free(&listeners[num_listeners]); + } + } + if (BThreadWorkDispatcher_UsingThreads(&twd)) { + BSecurity_GlobalFreeThreadSafe(); + } +fail4: + // NOTE: BThreadWorkDispatcher must be freed before NSPR and stuff + BThreadWorkDispatcher_Free(&twd); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&ss); +fail1: + if (options.ssl) { + CERT_DestroyCertificate(client_cert); + SECKEY_DestroyPrivateKey(client_key); +fail03: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail02: + SSL_ClearSessionCache(); + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail01: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish objects + DebugObjectGlobal_Finish(); + return 1; +} + +void terminate (void) +{ + BLog(BLOG_NOTICE, "tearing down"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--threads ]\n" + " [--use-threads-for-ssl-handshake]\n" + " [--use-threads-for-ssl-data]\n" + " [--ssl --nssdb --client-cert-name ]\n" + " [--server-name ]\n" + " --server-addr \n" + " [--tapdev ]\n" + " [--scope ] ...\n" + " [\n" + " --bind-addr \n" + " (transport-mode=udp? --num-ports )\n" + " [--ext-addr ] ...\n" + " ] ...\n" + " --transport-mode \n" + " (transport-mode=udp?\n" + " --encryption-mode \n" + " --hash-mode \n" + " [--otp ]\n" + " [--fragmentation-latency ]\n" + " )\n" + " (transport-mode=tcp?\n" + " (ssl? [--peer-ssl])\n" + " [--peer-tcp-socket-sndbuf ]\n" + " )\n" + " [--send-buffer-size ]\n" + " [--send-buffer-relay-size ]\n" + " [--max-macs ]\n" + " [--max-groups ]\n" + " [--igmp-group-membership-interval ]\n" + " [--igmp-last-member-query-time ]\n" + " [--allow-peer-talk-without-ssl]\n" + " [--max-peers ]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.threads = 0; + options.use_threads_for_ssl_handshake = 0; + options.use_threads_for_ssl_data = 0; + options.ssl = 0; + options.nssdb = NULL; + options.client_cert_name = NULL; + options.server_name = NULL; + options.server_addr = NULL; + options.tapdev = NULL; + options.num_scopes = 0; + options.num_bind_addrs = 0; + options.transport_mode = -1; + options.encryption_mode = -1; + options.hash_mode = -1; + options.otp_mode = SPPROTO_OTP_MODE_NONE; + options.fragmentation_latency = PEER_DEFAULT_UDP_FRAGMENTATION_LATENCY; + options.peer_ssl = 0; + options.peer_tcp_socket_sndbuf = -1; + options.send_buffer_size = PEER_DEFAULT_SEND_BUFFER_SIZE; + options.send_buffer_relay_size = PEER_DEFAULT_SEND_BUFFER_RELAY_SIZE; + options.max_macs = PEER_DEFAULT_MAX_MACS; + options.max_groups = PEER_DEFAULT_MAX_GROUPS; + options.igmp_group_membership_interval = DEFAULT_IGMP_GROUP_MEMBERSHIP_INTERVAL; + options.igmp_last_member_query_time = DEFAULT_IGMP_LAST_MEMBER_QUERY_TIME; + options.allow_peer_talk_without_ssl = 0; + options.max_peers = DEFAULT_MAX_PEERS; + + int have_fragmentation_latency = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--threads")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.threads = atoi(argv[i + 1]); + i++; + } + else if (!strcmp(arg, "--use-threads-for-ssl-handshake")) { + options.use_threads_for_ssl_handshake = 1; + } + else if (!strcmp(arg, "--use-threads-for-ssl-data")) { + options.use_threads_for_ssl_data = 1; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.client_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--tapdev")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.tapdev = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--scope")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_scopes == MAX_SCOPES) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.scopes[options.num_scopes] = argv[i + 1]; + options.num_scopes++; + i++; + } + else if (!strcmp(arg, "--bind-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_bind_addrs == MAX_BIND_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs]; + addr->addr = argv[i + 1]; + addr->num_ports = -1; + addr->num_ext_addrs = 0; + options.num_bind_addrs++; + i++; + } + else if (!strcmp(arg, "--num-ports")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_bind_addrs == 0) { + fprintf(stderr, "%s: must folow --bind-addr\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs - 1]; + if ((addr->num_ports = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--ext-addr")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + if (options.num_bind_addrs == 0) { + fprintf(stderr, "%s: must folow --bind-addr\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs - 1]; + if (addr->num_ext_addrs == MAX_EXT_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + struct ext_addr_option *eaddr = &addr->ext_addrs[addr->num_ext_addrs]; + eaddr->addr = argv[i + 1]; + eaddr->scope = argv[i + 2]; + addr->num_ext_addrs++; + i += 2; + } + else if (!strcmp(arg, "--transport-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "udp")) { + options.transport_mode = TRANSPORT_MODE_UDP; + } + else if (!strcmp(arg2, "tcp")) { + options.transport_mode = TRANSPORT_MODE_TCP; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--encryption-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "none")) { + options.encryption_mode = SPPROTO_ENCRYPTION_MODE_NONE; + } + else if (!strcmp(arg2, "blowfish")) { + options.encryption_mode = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(arg2, "aes")) { + options.encryption_mode = BENCRYPTION_CIPHER_AES; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--hash-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "none")) { + options.hash_mode = SPPROTO_HASH_MODE_NONE; + } + else if (!strcmp(arg2, "md5")) { + options.hash_mode = BHASH_TYPE_MD5; + } + else if (!strcmp(arg2, "sha1")) { + options.hash_mode = BHASH_TYPE_SHA1; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--otp")) { + if (3 >= argc - i) { + fprintf(stderr, "%s: requires three arguments\n", arg); + return 0; + } + char *otp_mode = argv[i + 1]; + char *otp_num = argv[i + 2]; + char *otp_num_warn = argv[i + 3]; + if (!strcmp(otp_mode, "blowfish")) { + options.otp_mode = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(otp_mode, "aes")) { + options.otp_mode = BENCRYPTION_CIPHER_AES; + } + else { + fprintf(stderr, "%s: wrong mode\n", arg); + return 0; + } + if ((options.otp_num = atoi(otp_num)) <= 0) { + fprintf(stderr, "%s: wrong num\n", arg); + return 0; + } + options.otp_num_warn = atoi(otp_num_warn); + if (options.otp_num_warn <= 0 || options.otp_num_warn > options.otp_num) { + fprintf(stderr, "%s: wrong num warn\n", arg); + return 0; + } + i += 3; + } + else if (!strcmp(arg, "--fragmentation-latency")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.fragmentation_latency = atoi(argv[i + 1]); + have_fragmentation_latency = 1; + i++; + } + else if (!strcmp(arg, "--peer-ssl")) { + options.peer_ssl = 1; + } + else if (!strcmp(arg, "--peer-tcp-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.peer_tcp_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--send-buffer-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.send_buffer_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--send-buffer-relay-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.send_buffer_relay_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-macs")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_macs = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-groups")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_groups = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--igmp-group-membership-interval")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.igmp_group_membership_interval = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--igmp-last-member-query-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.igmp_last_member_query_time = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-peers")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_peers = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--allow-peer-talk-without-ssl")) { + options.allow_peer_talk_without_ssl = 1; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.ssl != !!options.nssdb) { + fprintf(stderr, "False: --ssl <=> --nssdb\n"); + return 0; + } + + if (options.ssl != !!options.client_cert_name) { + fprintf(stderr, "False: --ssl <=> --client-cert-name\n"); + return 0; + } + + if (!options.server_addr) { + fprintf(stderr, "False: --server-addr\n"); + return 0; + } + + if (options.transport_mode < 0) { + fprintf(stderr, "False: --transport-mode\n"); + return 0; + } + + if ((options.transport_mode == TRANSPORT_MODE_UDP) != (options.encryption_mode >= 0)) { + fprintf(stderr, "False: UDP <=> --encryption-mode\n"); + return 0; + } + + if ((options.transport_mode == TRANSPORT_MODE_UDP) != (options.hash_mode >= 0)) { + fprintf(stderr, "False: UDP <=> --hash-mode\n"); + return 0; + } + + if (!(!(options.otp_mode != SPPROTO_OTP_MODE_NONE) || (options.transport_mode == TRANSPORT_MODE_UDP))) { + fprintf(stderr, "False: --otp => UDP\n"); + return 0; + } + + if (!(!have_fragmentation_latency || (options.transport_mode == TRANSPORT_MODE_UDP))) { + fprintf(stderr, "False: --fragmentation-latency => UDP\n"); + return 0; + } + + if (!(!options.peer_ssl || (options.ssl && options.transport_mode == TRANSPORT_MODE_TCP))) { + fprintf(stderr, "False: --peer-ssl => (--ssl && TCP)\n"); + return 0; + } + + if (!(!(options.peer_tcp_socket_sndbuf >= 0) || options.transport_mode == TRANSPORT_MODE_TCP)) { + fprintf(stderr, "False: --peer-tcp-socket-sndbuf => TCP\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve server address + ASSERT(options.server_addr) + if (!BAddr_Parse(&server_addr, options.server_addr, server_name, sizeof(server_name))) { + BLog(BLOG_ERROR, "server addr: BAddr_Parse failed"); + return 0; + } + + // override server name if requested + if (options.server_name) { + if (strlen(options.server_name) >= sizeof(server_name)) { + BLog(BLOG_ERROR, "server name: too long"); + return 0; + } + strcpy(server_name, options.server_name); + } + + // resolve bind addresses and external addresses + num_bind_addrs = 0; + for (int i = 0; i < options.num_bind_addrs; i++) { + struct bind_addr_option *addr = &options.bind_addrs[i]; + struct bind_addr *out = &bind_addrs[num_bind_addrs]; + + // read addr + if (!BAddr_Parse(&out->addr, addr->addr, NULL, 0)) { + BLog(BLOG_ERROR, "bind addr: BAddr_Parse failed"); + return 0; + } + + // read num ports + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (addr->num_ports < 0) { + BLog(BLOG_ERROR, "bind addr: num ports missing"); + return 0; + } + out->num_ports = addr->num_ports; + } + else if (addr->num_ports >= 0) { + BLog(BLOG_ERROR, "bind addr: num ports given, but not using UDP"); + return 0; + } + + // read ext addrs + out->num_ext_addrs = 0; + for (int j = 0; j < addr->num_ext_addrs; j++) { + struct ext_addr_option *eaddr = &addr->ext_addrs[j]; + struct ext_addr *eout = &out->ext_addrs[out->num_ext_addrs]; + + // read addr + if (string_begins_with(eaddr->addr, "{server_reported}:")) { + char *colon = strstr(eaddr->addr, ":"); + if ((eout->server_reported_port = atoi(colon + 1)) < 0) { + BLog(BLOG_ERROR, "ext addr: wrong port"); + return 0; + } + } else { + eout->server_reported_port = -1; + if (!BAddr_Parse(&eout->addr, eaddr->addr, NULL, 0)) { + BLog(BLOG_ERROR, "ext addr: BAddr_Parse failed"); + return 0; + } + if (!addr_supported(eout->addr)) { + BLog(BLOG_ERROR, "ext addr: addr_supported failed"); + return 0; + } + } + + // read scope + if (strlen(eaddr->scope) >= sizeof(eout->scope)) { + BLog(BLOG_ERROR, "ext addr: too long"); + return 0; + } + strcpy(eout->scope, eaddr->scope); + + out->num_ext_addrs++; + } + + num_bind_addrs++; + } + + // initialize SPProto parameters + if (options.transport_mode == TRANSPORT_MODE_UDP) { + sp_params.encryption_mode = options.encryption_mode; + sp_params.hash_mode = options.hash_mode; + sp_params.otp_mode = options.otp_mode; + if (options.otp_mode > 0) { + sp_params.otp_num = options.otp_num; + } + } + + return 1; +} + +int ssl_flags (void) +{ + int flags = 0; + if (options.use_threads_for_ssl_handshake) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE; + } + if (options.use_threads_for_ssl_data) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_IO; + } + return flags; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +void peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + ASSERT(num_peers < options.max_peers) + ASSERT(!find_peer_by_id(id)) + ASSERT(id != my_id) + ASSERT(cert_len >= 0) + ASSERT(cert_len <= SCID_NEWCLIENT_MAX_CERT_LEN) + + // allocate structure + struct peer_data *peer = (struct peer_data *)malloc(sizeof(*peer)); + if (!peer) { + BLog(BLOG_ERROR, "peer %d: failed to allocate memory", (int)id); + goto fail0; + } + + // remember id + peer->id = id; + + // remember flags + peer->flags = flags; + + // set no common name + peer->common_name = NULL; + + if (options.ssl) { + // remember certificate + memcpy(peer->cert, cert, cert_len); + peer->cert_len = cert_len; + + // make sure that CERT_DecodeCertFromPackage will interpretet the input as raw DER and not base64, + // in which case following workaroud wouldn't help + if (!(cert_len > 0 && (cert[0] & 0x1f) == 0x10)) { + peer_log(peer, BLOG_ERROR, "certificate does not look like DER"); + goto fail1; + } + + // copy the certificate and append it a good load of zero bytes, + // hopefully preventing the crappy CERT_DecodeCertFromPackage from crashing + // by reading past the of its input + uint8_t *certbuf = (uint8_t *)malloc(cert_len + 100); + if (!certbuf) { + peer_log(peer, BLOG_ERROR, "malloc failed"); + goto fail1; + } + memcpy(certbuf, cert, cert_len); + memset(certbuf + cert_len, 0, 100); + + // decode certificate, so we can extract the common name + CERTCertificate *nsscert = CERT_DecodeCertFromPackage((char *)certbuf, cert_len); + if (!nsscert) { + peer_log(peer, BLOG_ERROR, "CERT_DecodeCertFromPackage failed (%d)", PORT_GetError()); + free(certbuf); + goto fail1; + } + + free(certbuf); + + // remember common name + if (!(peer->common_name = CERT_GetCommonName(&nsscert->subject))) { + peer_log(peer, BLOG_ERROR, "CERT_GetCommonName failed"); + CERT_DestroyCertificate(nsscert); + goto fail1; + } + + CERT_DestroyCertificate(nsscert); + } + + // init and set init job (must be before initing server flow so we can send) + BPending_Init(&peer->job_init, BReactor_PendingGroup(&ss), (BPending_handler)peer_job_init, peer); + BPending_Set(&peer->job_init); + + // init server flow + if (!(peer->server_flow = server_flow_init())) { + peer_log(peer, BLOG_ERROR, "server_flow_init failed"); + goto fail2; + } + + if ((peer->flags & SCID_NEWCLIENT_FLAG_SSL) && !options.ssl) { + peer_log(peer, BLOG_ERROR, "peer requires talking with SSL, but we're not using SSL!?"); + goto fail3; + } + + if (options.ssl && !(peer->flags & SCID_NEWCLIENT_FLAG_SSL) && !options.allow_peer_talk_without_ssl) { + peer_log(peer, BLOG_ERROR, "peer requires talking without SSL, but we don't allow that"); + goto fail3; + } + + // choose chat SSL mode + int chat_ssl_mode = PEERCHAT_SSL_NONE; + if ((peer->flags & SCID_NEWCLIENT_FLAG_SSL)) { + chat_ssl_mode = (peer_am_master(peer) ? PEERCHAT_SSL_SERVER : PEERCHAT_SSL_CLIENT); + } + + // init chat + if (!PeerChat_Init(&peer->chat, peer->id, chat_ssl_mode, ssl_flags(), client_cert, client_key, peer->cert, peer->cert_len, BReactor_PendingGroup(&ss), &twd, peer, + (BLog_logfunc)peer_logfunc, + (PeerChat_handler_error)peer_chat_handler_error, + (PeerChat_handler_message)peer_chat_handler_message + )) { + peer_log(peer, BLOG_ERROR, "PeerChat_Init failed"); + goto fail3; + } + + // set no message + peer->chat_send_msg_len = -1; + + // connect server flow to chat + server_flow_connect(peer->server_flow, PeerChat_GetSendOutput(&peer->chat)); + + // set have chat + peer->have_chat = 1; + + // set have no resetpeer + peer->have_resetpeer = 0; + + // init local flow + if (!DataProtoFlow_Init(&peer->local_dpflow, &device_dpsource, my_id, peer->id, options.send_buffer_size, -1, NULL, NULL)) { + peer_log(peer, BLOG_ERROR, "DataProtoFlow_Init failed"); + goto fail4; + } + + // init frame decider peer + if (!FrameDeciderPeer_Init(&peer->decider_peer, &frame_decider, peer, (BLog_logfunc)peer_logfunc)) { + peer_log(peer, BLOG_ERROR, "FrameDeciderPeer_Init failed"); + goto fail5; + } + + // init receive peer + DPReceivePeer_Init(&peer->receive_peer, &device_output_dprd, peer->id, &peer->decider_peer, !!(peer->flags & SCID_NEWCLIENT_FLAG_RELAY_CLIENT)); + + // have no link + peer->have_link = 0; + + // have no relaying + peer->relaying_peer = NULL; + + // not waiting for relay + peer->waiting_relay = 0; + + // init reset timer + BTimer_Init(&peer->reset_timer, PEER_RETRY_TIME, (BTimer_handler)peer_reset_timer_handler, peer); + + // is not relay server + peer->is_relay = 0; + + // init binding + peer->binding = 0; + + // add to peers list + LinkedList1_Append(&peers, &peer->list_node); + num_peers++; + + switch (chat_ssl_mode) { + case PEERCHAT_SSL_NONE: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in plaintext mode"); + break; + case PEERCHAT_SSL_CLIENT: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in SSL client mode"); + break; + case PEERCHAT_SSL_SERVER: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in SSL server mode"); + break; + } + + return; + +fail5: + DataProtoFlow_Free(&peer->local_dpflow); +fail4: + server_flow_disconnect(peer->server_flow); + PeerChat_Free(&peer->chat); +fail3: + server_flow_free(peer->server_flow); +fail2: + BPending_Free(&peer->job_init); + if (peer->common_name) { + PORT_Free(peer->common_name); + } +fail1: + free(peer); +fail0: + return; +} + +void peer_remove (struct peer_data *peer, int exiting) +{ + peer_log(peer, BLOG_INFO, "removing"); + + // cleanup connections + peer_cleanup_connections(peer); + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + ASSERT(!peer->is_relay) + + // remove from peers list + LinkedList1_Remove(&peers, &peer->list_node); + num_peers--; + + // free reset timer + BReactor_RemoveTimer(&ss, &peer->reset_timer); + + // free receive peer + DPReceivePeer_Free(&peer->receive_peer); + + // free frame decider + FrameDeciderPeer_Free(&peer->decider_peer); + + // free local flow + DataProtoFlow_Free(&peer->local_dpflow); + + // free chat + if (peer->have_chat) { + peer_free_chat(peer); + } + + // free resetpeer + if (peer->have_resetpeer) { + // disconnect resetpeer source from server flow + server_flow_disconnect(peer->server_flow); + + // free resetpeer source + SinglePacketSource_Free(&peer->resetpeer_source); + } + + // free/die server flow + if (exiting || !PacketPassFairQueueFlow_IsBusy(&peer->server_flow->qflow)) { + server_flow_free(peer->server_flow); + } else { + server_flow_die(peer->server_flow); + } + + // free jobs + BPending_Free(&peer->job_init); + + // free common name + if (peer->common_name) { + PORT_Free(peer->common_name); + } + + // free peer structure + free(peer); +} + +void peer_logfunc (struct peer_data *peer) +{ + BLog_Append("peer %d", (int)peer->id); + if (peer->common_name) { + BLog_Append(" (%s)", peer->common_name); + } + BLog_Append(": "); +} + +void peer_log (struct peer_data *peer, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)peer_logfunc, peer, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +int peer_am_master (struct peer_data *peer) +{ + return (my_id > peer->id); +} + +void peer_free_chat (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + + // disconnect chat from server flow + server_flow_disconnect(peer->server_flow); + + // free chat + PeerChat_Free(&peer->chat); + + // set have no chat + peer->have_chat = 0; +} + +int peer_init_link (struct peer_data *peer) +{ + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + ASSERT(!peer->is_relay) + + // init receive receiver + DPReceiveReceiver_Init(&peer->receive_receiver, &peer->receive_peer); + PacketPassInterface *recv_if = DPReceiveReceiver_GetInput(&peer->receive_receiver); + + // init transport-specific link objects + PacketPassInterface *link_if; + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // init DatagramPeerIO + if (!DatagramPeerIO_Init( + &peer->pio.udp.pio, &ss, data_mtu, CLIENT_UDP_MTU, sp_params, + options.fragmentation_latency, PEER_UDP_ASSEMBLER_NUM_FRAMES, recv_if, + options.otp_num_warn, &twd, peer, + (BLog_logfunc)peer_logfunc, + (DatagramPeerIO_handler_error)peer_udp_pio_handler_error, + (DatagramPeerIO_handler_otp_warning)peer_udp_pio_handler_seed_warning, + (DatagramPeerIO_handler_otp_ready)peer_udp_pio_handler_seed_ready + )) { + peer_log(peer, BLOG_ERROR, "DatagramPeerIO_Init failed"); + goto fail1; + } + + if (SPPROTO_HAVE_OTP(sp_params)) { + // init send seed state + peer->pio.udp.sendseed_nextid = 0; + peer->pio.udp.sendseed_sent = 0; + + // init send seed job + BPending_Init(&peer->pio.udp.job_send_seed, BReactor_PendingGroup(&ss), (BPending_handler)peer_job_send_seed, peer); + } + + link_if = DatagramPeerIO_GetSendInput(&peer->pio.udp.pio); + } else { + // init StreamPeerIO + if (!StreamPeerIO_Init( + &peer->pio.tcp.pio, &ss, &twd, options.peer_ssl, ssl_flags(), + (options.peer_ssl ? peer->cert : NULL), + (options.peer_ssl ? peer->cert_len : -1), + data_mtu, + (options.peer_tcp_socket_sndbuf >= 0 ? options.peer_tcp_socket_sndbuf : PEER_DEFAULT_TCP_SOCKET_SNDBUF), + recv_if, + (BLog_logfunc)peer_logfunc, + (StreamPeerIO_handler_error)peer_tcp_pio_handler_error, peer + )) { + peer_log(peer, BLOG_ERROR, "StreamPeerIO_Init failed"); + goto fail1; + } + + link_if = StreamPeerIO_GetSendInput(&peer->pio.tcp.pio); + } + + // init sending + if (!DataProtoSink_Init(&peer->send_dp, &ss, link_if, PEER_KEEPALIVE_INTERVAL, PEER_KEEPALIVE_RECEIVE_TIMER, (DataProtoSink_handler)peer_dataproto_handler, peer)) { + peer_log(peer, BLOG_ERROR, "DataProto_Init failed"); + goto fail2; + } + + // attach local flow to our DataProtoSink + DataProtoFlow_Attach(&peer->local_dpflow, &peer->send_dp); + + // attach receive peer to our DataProtoSink + DPReceivePeer_AttachSink(&peer->receive_peer, &peer->send_dp); + + // set have link + peer->have_link = 1; + + return 1; + +fail2: + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Free(&peer->pio.udp.job_send_seed); + } + DatagramPeerIO_Free(&peer->pio.udp.pio); + } else { + StreamPeerIO_Free(&peer->pio.tcp.pio); + } +fail1: + DPReceiveReceiver_Free(&peer->receive_receiver); + return 0; +} + +void peer_free_link (struct peer_data *peer) +{ + ASSERT(peer->have_link) + ASSERT(!peer->is_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // detach receive peer from our DataProtoSink + DPReceivePeer_DetachSink(&peer->receive_peer); + + // detach local flow from our DataProtoSink + DataProtoFlow_Detach(&peer->local_dpflow); + + // free sending + DataProtoSink_Free(&peer->send_dp); + + // free transport-specific link objects + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Free(&peer->pio.udp.job_send_seed); + } + DatagramPeerIO_Free(&peer->pio.udp.pio); + } else { + StreamPeerIO_Free(&peer->pio.tcp.pio); + } + + // free receive receiver + DPReceiveReceiver_Free(&peer->receive_receiver); + + // set have no link + peer->have_link = 0; +} + +void peer_cleanup_connections (struct peer_data *peer) +{ + if (peer->have_link) { + if (peer->is_relay) { + peer_disable_relay_provider(peer); + } + peer_free_link(peer); + } + else if (peer->relaying_peer) { + peer_free_relaying(peer); + } + else if (peer->waiting_relay) { + peer_unregister_need_relay(peer); + } + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + ASSERT(!peer->is_relay) +} + +void peer_enable_relay_provider (struct peer_data *peer) +{ + ASSERT(peer->have_link) + ASSERT(!peer->is_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // add to relays list + LinkedList1_Append(&relays, &peer->relay_list_node); + + // init users list + LinkedList1_Init(&peer->relay_users); + + // set is relay + peer->is_relay = 1; + + // assign relays + assign_relays(); +} + +void peer_disable_relay_provider (struct peer_data *peer) +{ + ASSERT(peer->is_relay) + + ASSERT(peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // disconnect relay users + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&peer->relay_users)) { + struct peer_data *relay_user = UPPER_OBJECT(list_node, struct peer_data, relaying_list_node); + ASSERT(relay_user->relaying_peer == peer) + + // disconnect relay user + peer_free_relaying(relay_user); + + // add it to need relay list + peer_register_need_relay(relay_user); + } + + // remove from relays list + LinkedList1_Remove(&relays, &peer->relay_list_node); + + // set is not relay + peer->is_relay = 0; + + // assign relays + assign_relays(); +} + +void peer_install_relaying (struct peer_data *peer, struct peer_data *relay) +{ + ASSERT(!peer->relaying_peer) + ASSERT(!peer->have_link) + ASSERT(!peer->waiting_relay) + ASSERT(relay->is_relay) + + ASSERT(!peer->is_relay) + ASSERT(relay->have_link) + + peer_log(peer, BLOG_INFO, "installing relaying through %d", (int)relay->id); + + // add to relay's users list + LinkedList1_Append(&relay->relay_users, &peer->relaying_list_node); + + // attach local flow to relay + DataProtoFlow_Attach(&peer->local_dpflow, &relay->send_dp); + + // set relaying + peer->relaying_peer = relay; +} + +void peer_free_relaying (struct peer_data *peer) +{ + ASSERT(peer->relaying_peer) + + ASSERT(!peer->have_link) + ASSERT(!peer->waiting_relay) + + struct peer_data *relay = peer->relaying_peer; + ASSERT(relay->is_relay) + ASSERT(relay->have_link) + + peer_log(peer, BLOG_INFO, "uninstalling relaying through %d", (int)relay->id); + + // detach local flow from relay + DataProtoFlow_Detach(&peer->local_dpflow); + + // remove from relay's users list + LinkedList1_Remove(&relay->relay_users, &peer->relaying_list_node); + + // set not relaying + peer->relaying_peer = NULL; +} + +void peer_need_relay (struct peer_data *peer) +{ + ASSERT(!peer->is_relay) + + if (peer->waiting_relay) { + // already waiting for relay, do nothing + return; + } + + if (peer->have_link) { + peer_free_link(peer); + } + else if (peer->relaying_peer) { + peer_free_relaying(peer); + } + + // register the peer as needing a relay + peer_register_need_relay(peer); + + // assign relays + assign_relays(); +} + +void peer_register_need_relay (struct peer_data *peer) +{ + ASSERT(!peer->waiting_relay) + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + + ASSERT(!peer->is_relay) + + // add to need relay list + LinkedList1_Append(&waiting_relay_peers, &peer->waiting_relay_list_node); + + // set waiting relay + peer->waiting_relay = 1; +} + +void peer_unregister_need_relay (struct peer_data *peer) +{ + ASSERT(peer->waiting_relay) + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->is_relay) + + // remove from need relay list + LinkedList1_Remove(&waiting_relay_peers, &peer->waiting_relay_list_node); + + // set not waiting relay + peer->waiting_relay = 0; +} + +void peer_reset (struct peer_data *peer) +{ + peer_log(peer, BLOG_NOTICE, "resetting"); + + // cleanup connections + peer_cleanup_connections(peer); + + if (peer_am_master(peer)) { + // if we're the master, schedule retry + BReactor_SetTimer(&ss, &peer->reset_timer); + } else { + // if we're the slave, report to master + peer_send_simple(peer, MSGID_YOURETRY); + } +} + +void peer_resetpeer (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + ASSERT(!peer->have_resetpeer) + + // free chat + peer_free_chat(peer); + + // build resetpeer packet + struct packetproto_header pp_header; + struct sc_header sc_header; + struct sc_client_resetpeer sc_resetpeer; + pp_header.len = htol16(sizeof(struct sc_header) + sizeof(struct sc_client_resetpeer)); + sc_header.type = htol8(SCID_RESETPEER); + sc_resetpeer.clientid = htol16(peer->id); + memcpy(peer->resetpeer_packet, &pp_header, sizeof(pp_header)); + memcpy(peer->resetpeer_packet + sizeof(pp_header), &sc_header, sizeof(sc_header)); + memcpy(peer->resetpeer_packet + sizeof(pp_header) + sizeof(sc_header), &sc_resetpeer, sizeof(sc_resetpeer)); + + // init resetpeer sourse + SinglePacketSource_Init(&peer->resetpeer_source, peer->resetpeer_packet, sizeof(peer->resetpeer_packet), BReactor_PendingGroup(&ss)); + + // connect server flow to resetpeer source + server_flow_connect(peer->server_flow, SinglePacketSource_GetOutput(&peer->resetpeer_source)); + + // set have resetpeer + peer->have_resetpeer = 1; +} + +void peer_chat_handler_error (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + ASSERT(!peer->have_resetpeer) + + peer_log(peer, BLOG_ERROR, "chat error, sending resetpeer"); + + peer_resetpeer(peer); +} + +void peer_chat_handler_message (struct peer_data *peer, uint8_t *data, int data_len) +{ + ASSERT(peer->have_chat) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // parse message + msgParser parser; + if (!msgParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_NOTICE, "msg: failed to parse"); + return; + } + + // read message + uint16_t type = 0; // to remove warning + ASSERT_EXECUTE(msgParser_Gettype(&parser, &type)) + uint8_t *payload = NULL; // to remove warning + int payload_len = 0; // to remove warning + ASSERT_EXECUTE(msgParser_Getpayload(&parser, &payload, &payload_len)) + + // dispatch according to message type + switch (type) { + case MSGID_YOUCONNECT: + peer_msg_youconnect(peer, payload, payload_len); + return; + case MSGID_CANNOTCONNECT: + peer_msg_cannotconnect(peer, payload, payload_len); + return; + case MSGID_CANNOTBIND: + peer_msg_cannotbind(peer, payload, payload_len); + return; + case MSGID_YOURETRY: + peer_msg_youretry(peer, payload, payload_len); + return; + case MSGID_SEED: + peer_msg_seed(peer, payload, payload_len); + return; + case MSGID_CONFIRMSEED: + peer_msg_confirmseed(peer, payload, payload_len); + return; + default: + BLog(BLOG_NOTICE, "msg: unknown type"); + return; + } +} + +void peer_msg_youconnect (struct peer_data *peer, uint8_t *data, int data_len) +{ + // init parser + msg_youconnectParser parser; + if (!msg_youconnectParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to parse"); + return; + } + + // try addresses + BAddr addr; + while (1) { + // get address message + uint8_t *addrmsg_data; + int addrmsg_len; + if (!msg_youconnectParser_Getaddr(&parser, &addrmsg_data, &addrmsg_len)) { + peer_log(peer, BLOG_NOTICE, "msg_youconnect: no usable addresses"); + peer_send_simple(peer, MSGID_CANNOTCONNECT); + return; + } + + // parse address message + msg_youconnect_addrParser aparser; + if (!msg_youconnect_addrParser_Init(&aparser, addrmsg_data, addrmsg_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to parse address message"); + return; + } + + // check if the address scope is known + uint8_t *name_data = NULL; // to remove warning + int name_len = 0; // to remove warning + ASSERT_EXECUTE(msg_youconnect_addrParser_Getname(&aparser, &name_data, &name_len)) + char *name; + if (!(name = address_scope_known(name_data, name_len))) { + continue; + } + + // read address + uint8_t *addr_data = NULL; // to remove warning + int addr_len = 0; // to remove warning + ASSERT_EXECUTE(msg_youconnect_addrParser_Getaddr(&aparser, &addr_data, &addr_len)) + if (!addr_read(addr_data, addr_len, &addr)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to read address"); + continue; + } + + peer_log(peer, BLOG_NOTICE, "msg_youconnect: using address in scope '%s'", name); + break; + } + + // discard further addresses + msg_youconnectParser_Forwardaddr(&parser); + + uint8_t *key = NULL; + uint64_t password = 0; + + // read additonal parameters + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + int key_len; + if (!msg_youconnectParser_Getkey(&parser, &key, &key_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: no key"); + return; + } + if (key_len != BEncryption_cipher_key_size(sp_params.encryption_mode)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: wrong key size"); + return; + } + } + } else { + if (!msg_youconnectParser_Getpassword(&parser, &password)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: no password"); + return; + } + } + + if (!msg_youconnectParser_GotEverything(&parser)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: stray data"); + return; + } + + peer_log(peer, BLOG_INFO, "connecting"); + + peer_connect(peer, addr, key, password); +} + +void peer_msg_cannotconnect (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_cannotconnect: invalid length"); + return; + } + + if (!peer->binding) { + peer_log(peer, BLOG_WARNING, "msg_cannotconnect: not binding"); + return; + } + + peer_log(peer, BLOG_INFO, "peer could not connect"); + + // continue trying bind addresses + peer_bind(peer); + return; +} + +void peer_msg_cannotbind (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_cannotbind: invalid length"); + return; + } + + peer_log(peer, BLOG_INFO, "peer cannot bind"); + + if (!peer_am_master(peer)) { + peer_start_binding(peer); + } else { + if (!peer->is_relay) { + peer_need_relay(peer); + } + } +} + +void peer_msg_seed (struct peer_data *peer, uint8_t *data, int data_len) +{ + msg_seedParser parser; + if (!msg_seedParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_seed: failed to parse"); + return; + } + + // read message + uint16_t seed_id = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getseed_id(&parser, &seed_id)) + uint8_t *key = NULL; // to remove warning + int key_len = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getkey(&parser, &key, &key_len)) + uint8_t *iv = NULL; // to remove warning + int iv_len = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getiv(&parser, &iv, &iv_len)) + + if (options.transport_mode != TRANSPORT_MODE_UDP) { + peer_log(peer, BLOG_WARNING, "msg_seed: not in UDP mode"); + return; + } + + if (!SPPROTO_HAVE_OTP(sp_params)) { + peer_log(peer, BLOG_WARNING, "msg_seed: OTPs disabled"); + return; + } + + if (key_len != BEncryption_cipher_key_size(sp_params.otp_mode)) { + peer_log(peer, BLOG_WARNING, "msg_seed: wrong key length"); + return; + } + + if (iv_len != BEncryption_cipher_block_size(sp_params.otp_mode)) { + peer_log(peer, BLOG_WARNING, "msg_seed: wrong IV length"); + return; + } + + if (!peer->have_link) { + peer_log(peer, BLOG_WARNING, "msg_seed: have no link"); + return; + } + + peer_log(peer, BLOG_DEBUG, "received OTP receive seed"); + + // add receive seed + DatagramPeerIO_AddOTPRecvSeed(&peer->pio.udp.pio, seed_id, key, iv); + + // remember seed ID so we can confirm it from peer_udp_pio_handler_seed_ready + peer->pio.udp.pending_recvseed_id = seed_id; +} + +void peer_msg_confirmseed (struct peer_data *peer, uint8_t *data, int data_len) +{ + msg_confirmseedParser parser; + if (!msg_confirmseedParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: failed to parse"); + return; + } + + // read message + uint16_t seed_id = 0; // to remove warning + ASSERT_EXECUTE(msg_confirmseedParser_Getseed_id(&parser, &seed_id)) + + if (options.transport_mode != TRANSPORT_MODE_UDP) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: not in UDP mode"); + return; + } + + if (!SPPROTO_HAVE_OTP(sp_params)) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: OTPs disabled"); + return; + } + + if (!peer->have_link) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: have no link"); + return; + } + + if (!peer->pio.udp.sendseed_sent) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: no seed has been sent"); + return; + } + + if (seed_id != peer->pio.udp.sendseed_sent_id) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: invalid seed: expecting %d, received %d", (int)peer->pio.udp.sendseed_sent_id, (int)seed_id); + return; + } + + peer_log(peer, BLOG_DEBUG, "OTP send seed confirmed"); + + // no longer waiting for confirmation + peer->pio.udp.sendseed_sent = 0; + + // start using the seed + DatagramPeerIO_SetOTPSendSeed(&peer->pio.udp.pio, peer->pio.udp.sendseed_sent_id, peer->pio.udp.sendseed_sent_key, peer->pio.udp.sendseed_sent_iv); +} + +void peer_msg_youretry (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_youretry: invalid length"); + return; + } + + if (!peer_am_master(peer)) { + peer_log(peer, BLOG_WARNING, "msg_youretry: we are not master"); + return; + } + + peer_log(peer, BLOG_NOTICE, "requests reset"); + + peer_reset(peer); +} + +void peer_udp_pio_handler_seed_warning (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + + // generate and send a new seed + if (!peer->pio.udp.sendseed_sent) { + BPending_Set(&peer->pio.udp.job_send_seed); + } +} + +void peer_udp_pio_handler_seed_ready (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + + // send confirmation + peer_send_confirmseed(peer, peer->pio.udp.pending_recvseed_id); +} + +void peer_udp_pio_handler_error (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(peer->have_link) + + peer_log(peer, BLOG_NOTICE, "UDP connection failed"); + + peer_reset(peer); + return; +} + +void peer_tcp_pio_handler_error (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_TCP) + ASSERT(peer->have_link) + + peer_log(peer, BLOG_NOTICE, "TCP connection failed"); + + peer_reset(peer); + return; +} + +void peer_reset_timer_handler (struct peer_data *peer) +{ + ASSERT(peer_am_master(peer)) + + BLog(BLOG_NOTICE, "retry timer expired"); + + // start setup process + peer_start_binding(peer); +} + +void peer_start_binding (struct peer_data *peer) +{ + peer->binding = 1; + peer->binding_addrpos = 0; + + peer_bind(peer); +} + +void peer_bind (struct peer_data *peer) +{ + ASSERT(peer->binding) + ASSERT(peer->binding_addrpos >= 0) + ASSERT(peer->binding_addrpos <= num_bind_addrs) + + while (peer->binding_addrpos < num_bind_addrs) { + // if there are no external addresses, skip bind address + if (bind_addrs[peer->binding_addrpos].num_ext_addrs == 0) { + peer->binding_addrpos++; + continue; + } + + // try to bind + int cont; + peer_bind_one_address(peer, peer->binding_addrpos, &cont); + + // increment address counter + peer->binding_addrpos++; + + if (!cont) { + return; + } + } + + peer_log(peer, BLOG_NOTICE, "no more addresses to bind to"); + + // no longer binding + peer->binding = 0; + + // tell the peer we failed to bind + peer_send_simple(peer, MSGID_CANNOTBIND); + + // if we are the slave, setup relaying + if (!peer_am_master(peer)) { + if (!peer->is_relay) { + peer_need_relay(peer); + } + } +} + +void peer_bind_one_address (struct peer_data *peer, int addr_index, int *cont) +{ + ASSERT(addr_index >= 0) + ASSERT(addr_index < num_bind_addrs) + ASSERT(bind_addrs[addr_index].num_ext_addrs > 0) + + // get a fresh link + peer_cleanup_connections(peer); + if (!peer_init_link(peer)) { + peer_log(peer, BLOG_ERROR, "cannot get link"); + *cont = 0; + peer_reset(peer); + return; + } + + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // get addr + struct bind_addr *addr = &bind_addrs[addr_index]; + + // try binding to all ports in the range + int port_add; + for (port_add = 0; port_add < addr->num_ports; port_add++) { + BAddr tryaddr = addr->addr; + BAddr_SetPort(&tryaddr, hton16(ntoh16(BAddr_GetPort(&tryaddr)) + port_add)); + if (DatagramPeerIO_Bind(&peer->pio.udp.pio, tryaddr)) { + break; + } + } + if (port_add == addr->num_ports) { + BLog(BLOG_NOTICE, "failed to bind to any port"); + *cont = 1; + return; + } + + uint8_t key[BENCRYPTION_MAX_KEY_SIZE]; + + // generate and set encryption key + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + BRandom_randomize(key, BEncryption_cipher_key_size(sp_params.encryption_mode)); + DatagramPeerIO_SetEncryptionKey(&peer->pio.udp.pio, key); + } + + // schedule sending OTP seed + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Set(&peer->pio.udp.job_send_seed); + } + + // send connectinfo + peer_send_conectinfo(peer, addr_index, port_add, key, 0); + } else { + // order StreamPeerIO to listen + uint64_t pass; + StreamPeerIO_Listen(&peer->pio.tcp.pio, &listeners[addr_index], &pass); + + // send connectinfo + peer_send_conectinfo(peer, addr_index, 0, NULL, pass); + } + + peer_log(peer, BLOG_NOTICE, "bound to address number %d", addr_index); + + *cont = 0; +} + +void peer_connect (struct peer_data *peer, BAddr addr, uint8_t* encryption_key, uint64_t password) +{ + // get a fresh link + peer_cleanup_connections(peer); + if (!peer_init_link(peer)) { + peer_log(peer, BLOG_ERROR, "cannot get link"); + peer_reset(peer); + return; + } + + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // order DatagramPeerIO to connect + if (!DatagramPeerIO_Connect(&peer->pio.udp.pio, addr)) { + peer_log(peer, BLOG_NOTICE, "DatagramPeerIO_Connect failed"); + peer_reset(peer); + return; + } + + // set encryption key + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + DatagramPeerIO_SetEncryptionKey(&peer->pio.udp.pio, encryption_key); + } + + // generate and send a send seed + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Set(&peer->pio.udp.job_send_seed); + } + } else { + // order StreamPeerIO to connect + if (!StreamPeerIO_Connect(&peer->pio.tcp.pio, addr, password, client_cert, client_key)) { + peer_log(peer, BLOG_NOTICE, "StreamPeerIO_Connect failed"); + peer_reset(peer); + return; + } + } +} + +static int peer_start_msg (struct peer_data *peer, void **data, int type, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= MSG_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + ASSERT(peer->chat_send_msg_len == -1) + + // make sure we have chat + if (!peer->have_chat) { + peer_log(peer, BLOG_ERROR, "cannot send message, chat is down"); + return 0; + } + +#ifdef SIMULATE_PEER_OUT_OF_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_PEER_OUT_OF_BUFFER) { + peer_log(peer, BLOG_ERROR, "simulating out of buffer, sending resetpeer"); + peer_resetpeer(peer); + return 0; + } +#endif + + // obtain buffer location + uint8_t *packet; + if (!PeerChat_StartMessage(&peer->chat, &packet)) { + peer_log(peer, BLOG_ERROR, "cannot send message, out of buffer, sending resetpeer"); + peer_resetpeer(peer); + return 0; + } + + // write fields + msgWriter writer; + msgWriter_Init(&writer, packet); + msgWriter_Addtype(&writer, type); + uint8_t *payload_dst = msgWriter_Addpayload(&writer, len); + msgWriter_Finish(&writer); + + // set have message + peer->chat_send_msg_len = len; + + if (data) { + *data = payload_dst; + } + return 1; +} + +static void peer_end_msg (struct peer_data *peer) +{ + ASSERT(peer->chat_send_msg_len >= 0) + ASSERT(peer->have_chat) + + // submit packet to buffer + PeerChat_EndMessage(&peer->chat, msg_SIZEtype + msg_SIZEpayload(peer->chat_send_msg_len)); + + // set no message + peer->chat_send_msg_len = -1; +} + +void peer_send_simple (struct peer_data *peer, int msgid) +{ + if (!peer_start_msg(peer, NULL, msgid, 0)) { + return; + } + peer_end_msg(peer); +} + +void peer_send_conectinfo (struct peer_data *peer, int addr_index, int port_adjust, uint8_t *enckey, uint64_t pass) +{ + ASSERT(addr_index >= 0) + ASSERT(addr_index < num_bind_addrs) + ASSERT(bind_addrs[addr_index].num_ext_addrs > 0) + + // get address + struct bind_addr *bind_addr = &bind_addrs[addr_index]; + + // remember encryption key size + int key_size = 0; // to remove warning + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + key_size = BEncryption_cipher_key_size(sp_params.encryption_mode); + } + + // calculate message length .. + int msg_len = 0; + + // addresses + for (int i = 0; i < bind_addr->num_ext_addrs; i++) { + int addrmsg_len = + msg_youconnect_addr_SIZEname(strlen(bind_addr->ext_addrs[i].scope)) + + msg_youconnect_addr_SIZEaddr(addr_size(bind_addr->ext_addrs[i].addr)); + msg_len += msg_youconnect_SIZEaddr(addrmsg_len); + } + + // encryption key + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + msg_len += msg_youconnect_SIZEkey(key_size); + } + + // password + if (options.transport_mode == TRANSPORT_MODE_TCP) { + msg_len += msg_youconnect_SIZEpassword; + } + + // check if it's too big (because of the addresses) + if (msg_len > MSG_MAX_PAYLOAD) { + BLog(BLOG_ERROR, "cannot send too big youconnect message"); + return; + } + + // start message + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_YOUCONNECT, msg_len)) { + return; + } + + // init writer + msg_youconnectWriter writer; + msg_youconnectWriter_Init(&writer, msg); + + // write addresses + for (int i = 0; i < bind_addr->num_ext_addrs; i++) { + int name_len = strlen(bind_addr->ext_addrs[i].scope); + int addr_len = addr_size(bind_addr->ext_addrs[i].addr); + + // get a pointer for writing the address + int addrmsg_len = + msg_youconnect_addr_SIZEname(name_len) + + msg_youconnect_addr_SIZEaddr(addr_len); + uint8_t *addrmsg_dst = msg_youconnectWriter_Addaddr(&writer, addrmsg_len); + + // init address writer + msg_youconnect_addrWriter awriter; + msg_youconnect_addrWriter_Init(&awriter, addrmsg_dst); + + // write scope + uint8_t *name_dst = msg_youconnect_addrWriter_Addname(&awriter, name_len); + memcpy(name_dst, bind_addr->ext_addrs[i].scope, name_len); + + // write address with adjusted port + BAddr addr = bind_addr->ext_addrs[i].addr; + BAddr_SetPort(&addr, hton16(ntoh16(BAddr_GetPort(&addr)) + port_adjust)); + uint8_t *addr_dst = msg_youconnect_addrWriter_Addaddr(&awriter, addr_len); + addr_write(addr_dst, addr); + + // finish address writer + msg_youconnect_addrWriter_Finish(&awriter); + } + + // write encryption key + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + uint8_t *key_dst = msg_youconnectWriter_Addkey(&writer, key_size); + memcpy(key_dst, enckey, key_size); + } + + // write password + if (options.transport_mode == TRANSPORT_MODE_TCP) { + msg_youconnectWriter_Addpassword(&writer, pass); + } + + // finish writer + msg_youconnectWriter_Finish(&writer); + + // end message + peer_end_msg(peer); +} + +void peer_send_confirmseed (struct peer_data *peer, uint16_t seed_id) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + + // send confirmation + int msg_len = msg_confirmseed_SIZEseed_id; + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_CONFIRMSEED, msg_len)) { + return; + } + msg_confirmseedWriter writer; + msg_confirmseedWriter_Init(&writer, msg); + msg_confirmseedWriter_Addseed_id(&writer, seed_id); + msg_confirmseedWriter_Finish(&writer); + peer_end_msg(peer); +} + +void peer_dataproto_handler (struct peer_data *peer, int up) +{ + ASSERT(peer->have_link) + + if (up) { + peer_log(peer, BLOG_INFO, "up"); + + // if it can be a relay provided, enable it + if ((peer->flags & SCID_NEWCLIENT_FLAG_RELAY_SERVER) && !peer->is_relay) { + peer_enable_relay_provider(peer); + } + } else { + peer_log(peer, BLOG_INFO, "down"); + + // if it is a relay provider, disable it + if (peer->is_relay) { + peer_disable_relay_provider(peer); + } + } +} + +struct peer_data * find_peer_by_id (peerid_t id) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&peers); node; node = LinkedList1Node_Next(node)) { + struct peer_data *peer = UPPER_OBJECT(node, struct peer_data, list_node); + if (peer->id == id) { + return peer; + } + } + + return NULL; +} + +void device_error_handler (void *unused) +{ + BLog(BLOG_ERROR, "device error"); + + terminate(); +} + +void device_dpsource_handler (void *unused, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + ASSERT(frame_len <= device_mtu) + + // give frame to decider + FrameDecider_AnalyzeAndDecide(&frame_decider, frame, frame_len); + + // forward frame to peers + FrameDeciderPeer *decider_peer = FrameDecider_NextDestination(&frame_decider); + while (decider_peer) { + FrameDeciderPeer *next = FrameDecider_NextDestination(&frame_decider); + struct peer_data *peer = UPPER_OBJECT(decider_peer, struct peer_data, decider_peer); + DataProtoFlow_Route(&peer->local_dpflow, !!next); + decider_peer = next; + } +} + +void assign_relays (void) +{ + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&waiting_relay_peers)) { + struct peer_data *peer = UPPER_OBJECT(list_node, struct peer_data, waiting_relay_list_node); + ASSERT(peer->waiting_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->have_link) + + // get a relay + LinkedList1Node *list_node2 = LinkedList1_GetFirst(&relays); + if (!list_node2) { + BLog(BLOG_NOTICE, "no relays"); + return; + } + struct peer_data *relay = UPPER_OBJECT(list_node2, struct peer_data, relay_list_node); + ASSERT(relay->is_relay) + + // no longer waiting for relay + peer_unregister_need_relay(peer); + + // install the relay + peer_install_relaying(peer, relay); + } +} + +char * address_scope_known (uint8_t *name, int name_len) +{ + ASSERT(name_len >= 0) + + for (int i = 0; i < options.num_scopes; i++) { + if (name_len == strlen(options.scopes[i]) && !memcmp(name, options.scopes[i], name_len)) { + return options.scopes[i]; + } + } + + return NULL; +} + +void server_handler_error (void *user) +{ + BLog(BLOG_ERROR, "server connection failed, exiting"); + + terminate(); +} + +void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip) +{ + ASSERT(!server_ready) + + // remember our ID + my_id = param_my_id; + + // store server reported addresses + for (int i = 0; i < num_bind_addrs; i++) { + struct bind_addr *addr = &bind_addrs[i]; + for (int j = 0; j < addr->num_ext_addrs; j++) { + struct ext_addr *eaddr = &addr->ext_addrs[j]; + if (eaddr->server_reported_port >= 0) { + if (ext_ip == 0) { + BLog(BLOG_ERROR, "server did not provide our address"); + terminate(); + return; + } + BAddr_InitIPv4(&eaddr->addr, ext_ip, hton16(eaddr->server_reported_port)); + char str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&eaddr->addr, str); + BLog(BLOG_INFO, "external address (%d,%d): server reported %s", i, j, str); + } + } + } + + // give receive device the ID + DPReceiveDevice_SetPeerID(&device_output_dprd, my_id); + + // init server queue + if (!PacketPassFairQueue_Init(&server_queue, ServerConnection_GetSendInterface(&server), BReactor_PendingGroup(&ss), 0, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + terminate(); + return; + } + + // set server ready + server_ready = 1; + + BLog(BLOG_INFO, "server: ready, my ID is %d", (int)my_id); +} + +void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + ASSERT(cert_len >= 0) + ASSERT(cert_len <= SCID_NEWCLIENT_MAX_CERT_LEN) + + // check if the peer already exists + if (find_peer_by_id(peer_id)) { + BLog(BLOG_WARNING, "server: newclient: peer already known"); + return; + } + + // make sure it's not the same ID as us + if (peer_id == my_id) { + BLog(BLOG_WARNING, "server: newclient: peer has our ID"); + return; + } + + // check if there is spece for the peer + if (num_peers >= options.max_peers) { + BLog(BLOG_WARNING, "server: newclient: no space for new peer (maximum number reached)"); + return; + } + + if (!options.ssl && cert_len > 0) { + BLog(BLOG_WARNING, "server: newclient: certificate supplied, but not using TLS"); + return; + } + + peer_add(peer_id, flags, cert, cert_len); +} + +void server_handler_endclient (void *user, peerid_t peer_id) +{ + ASSERT(server_ready) + + // find peer + struct peer_data *peer = find_peer_by_id(peer_id); + if (!peer) { + BLog(BLOG_WARNING, "server: endclient: peer %d not known", (int)peer_id); + return; + } + + // remove peer + peer_remove(peer, 0); +} + +void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len) +{ + ASSERT(server_ready) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // find peer + struct peer_data *peer = find_peer_by_id(peer_id); + if (!peer) { + BLog(BLOG_WARNING, "server: message: peer not known"); + return; + } + + // make sure we have chat + if (!peer->have_chat) { + peer_log(peer, BLOG_ERROR, "cannot process message, chat is down"); + return; + } + + // pass message to chat + PeerChat_InputReceived(&peer->chat, data, data_len); +} + +void peer_job_send_seed (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + ASSERT(!peer->pio.udp.sendseed_sent) + + peer_log(peer, BLOG_DEBUG, "sending OTP send seed"); + + int key_len = BEncryption_cipher_key_size(sp_params.otp_mode); + int iv_len = BEncryption_cipher_block_size(sp_params.otp_mode); + + // generate seed + peer->pio.udp.sendseed_sent_id = peer->pio.udp.sendseed_nextid; + BRandom_randomize(peer->pio.udp.sendseed_sent_key, key_len); + BRandom_randomize(peer->pio.udp.sendseed_sent_iv, iv_len); + + // set as sent, increment next seed ID + peer->pio.udp.sendseed_sent = 1; + peer->pio.udp.sendseed_nextid++; + + // send seed to the peer + int msg_len = msg_seed_SIZEseed_id + msg_seed_SIZEkey(key_len) + msg_seed_SIZEiv(iv_len); + if (msg_len > MSG_MAX_PAYLOAD) { + peer_log(peer, BLOG_ERROR, "OTP send seed message too big"); + return; + } + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_SEED, msg_len)) { + return; + } + msg_seedWriter writer; + msg_seedWriter_Init(&writer, msg); + msg_seedWriter_Addseed_id(&writer, peer->pio.udp.sendseed_sent_id); + uint8_t *key_dst = msg_seedWriter_Addkey(&writer, key_len); + memcpy(key_dst, peer->pio.udp.sendseed_sent_key, key_len); + uint8_t *iv_dst = msg_seedWriter_Addiv(&writer, iv_len); + memcpy(iv_dst, peer->pio.udp.sendseed_sent_iv, iv_len); + msg_seedWriter_Finish(&writer); + peer_end_msg(peer); +} + +void peer_job_init (struct peer_data *peer) +{ + // start setup process + if (peer_am_master(peer)) { + peer_start_binding(peer); + } +} + +struct server_flow * server_flow_init (void) +{ + ASSERT(server_ready) + + // allocate structure + struct server_flow *flow = (struct server_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init queue flow + PacketPassFairQueueFlow_Init(&flow->qflow, &server_queue); + + // init connector + PacketRecvConnector_Init(&flow->connector, sizeof(struct packetproto_header) + SC_MAX_ENC, BReactor_PendingGroup(&ss)); + + // init encoder buffer + if (!SinglePacketBuffer_Init(&flow->encoder_buffer, PacketRecvConnector_GetOutput(&flow->connector), PacketPassFairQueueFlow_GetInput(&flow->qflow), BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail1; + } + + // set not connected + flow->connected = 0; + + return flow; + +fail1: + PacketRecvConnector_Free(&flow->connector); + PacketPassFairQueueFlow_Free(&flow->qflow); + free(flow); +fail0: + return NULL; +} + +void server_flow_free (struct server_flow *flow) +{ + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + ASSERT(!flow->connected) + + // remove dying flow reference + if (flow == dying_server_flow) { + dying_server_flow = NULL; + } + + // free encoder buffer + SinglePacketBuffer_Free(&flow->encoder_buffer); + + // free connector + PacketRecvConnector_Free(&flow->connector); + + // free queue flow + PacketPassFairQueueFlow_Free(&flow->qflow); + + // free structure + free(flow); +} + +void server_flow_die (struct server_flow *flow) +{ + ASSERT(PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + ASSERT(!flow->connected) + ASSERT(!dying_server_flow) + + // request notification when flow is done + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)server_flow_qflow_handler_busy, flow); + + // set dying flow + dying_server_flow = flow; +} + +void server_flow_qflow_handler_busy (struct server_flow *flow) +{ + ASSERT(flow == dying_server_flow) + ASSERT(!flow->connected) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + // finally free flow + server_flow_free(flow); +} + +void server_flow_connect (struct server_flow *flow, PacketRecvInterface *input) +{ + ASSERT(!flow->connected) + ASSERT(flow != dying_server_flow) + + // connect input + PacketRecvConnector_ConnectInput(&flow->connector, input); + + // set connected + flow->connected = 1; +} + +void server_flow_disconnect (struct server_flow *flow) +{ + ASSERT(flow->connected) + ASSERT(flow != dying_server_flow) + + // disconnect input + PacketRecvConnector_DisconnectInput(&flow->connector); + + // set not connected + flow->connected = 0; +} diff --git a/app/src/main/jni/badvpn/client/client.h b/app/src/main/jni/badvpn/client/client.h new file mode 100644 index 0000000..595ed59 --- /dev/null +++ b/app/src/main/jni/badvpn/client/client.h @@ -0,0 +1,193 @@ +/** + * @file client.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: all time values are in milliseconds + +// name of the program +#define PROGRAM_NAME "client" + +// server output buffer size +#define SERVER_BUFFER_MIN_PACKETS 200 + +// maximum UDP payload size +#define CLIENT_UDP_MTU 1472 + +// maximum number of pending TCP PasswordListener clients +#define TCP_MAX_PASSWORD_LISTENER_CLIENTS 50 + +// maximum number of peers +#define DEFAULT_MAX_PEERS 256 +// maximum number of peer's MAC addresses to remember +#define PEER_DEFAULT_MAX_MACS 16 +// maximum number of multicast addresses per peer +#define PEER_DEFAULT_MAX_GROUPS 16 +// how long we wait for a packet to reach full size before sending it (see FragmentProtoDisassembler latency argument) +#define PEER_DEFAULT_UDP_FRAGMENTATION_LATENCY 0 +// value related to how much out-of-order input we tolerate (see FragmentProtoAssembler num_frames argument) +#define PEER_UDP_ASSEMBLER_NUM_FRAMES 4 +// socket send buffer (SO_SNDBUF) for peer TCP connections, <=0 to not set +#define PEER_DEFAULT_TCP_SOCKET_SNDBUF 1048576 +// keep-alive packet interval for p2p communication +#define PEER_KEEPALIVE_INTERVAL 10000 +// keep-alive receive timer for p2p communication (after how long to consider the link down) +#define PEER_KEEPALIVE_RECEIVE_TIMER 22000 +// size of frame send buffer, in number of frames +#define PEER_DEFAULT_SEND_BUFFER_SIZE 32 +// size of frame send buffer for relayed packets, in number of frames +#define PEER_DEFAULT_SEND_BUFFER_RELAY_SIZE 32 +// time after an unused relay flow is freed (-1 for never) +#define PEER_RELAY_FLOW_INACTIVITY_TIME 10000 +// retry time +#define PEER_RETRY_TIME 5000 + +// for how long a peer can send no Membership Reports for a group +// before the peer and group are disassociated +#define DEFAULT_IGMP_GROUP_MEMBERSHIP_INTERVAL 260000 +// how long to wait for joins after a Group Specific query has been +// forwarded to a peer before assuming there are no listeners at the peer +#define DEFAULT_IGMP_LAST_MEMBER_QUERY_TIME 2000 + +// maximum bind addresses +#define MAX_BIND_ADDRS 8 +// maximum external addresses per bind address +#define MAX_EXT_ADDRS 8 +// maximum scopes +#define MAX_SCOPES 8 + +//#define SIMULATE_PEER_OUT_OF_BUFFER 70 + +struct server_flow { + PacketPassFairQueueFlow qflow; + SinglePacketBuffer encoder_buffer; + PacketRecvConnector connector; + int connected; +}; + +struct peer_data { + // peer identifier + peerid_t id; + + // flags provided by the server + int flags; + + // certificate reported by the server, defined only if using SSL + uint8_t cert[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_len; + char *common_name; + + // init job + BPending job_init; + + // server flow + struct server_flow *server_flow; + + // chat + int have_chat; + PeerChat chat; + int chat_send_msg_len; + + // resetpeer source (when chat fails) + int have_resetpeer; + uint8_t resetpeer_packet[sizeof(struct packetproto_header) + sizeof(struct sc_header) + sizeof(struct sc_client_resetpeer)]; + SinglePacketSource resetpeer_source; + + // local flow + DataProtoFlow local_dpflow; + + // frame decider peer + FrameDeciderPeer decider_peer; + + // receive peer + DPReceivePeer receive_peer; + + // flag if link objects are initialized + int have_link; + + // receive receiver + DPReceiveReceiver receive_receiver; + + // transport-specific link objects + union { + struct { + DatagramPeerIO pio; + uint16_t sendseed_nextid; + int sendseed_sent; + uint16_t sendseed_sent_id; + uint8_t sendseed_sent_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t sendseed_sent_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + uint16_t pending_recvseed_id; + BPending job_send_seed; + } udp; + struct { + StreamPeerIO pio; + } tcp; + } pio; + + // link sending + DataProtoSink send_dp; + + // relaying objects + struct peer_data *relaying_peer; // peer through which we are relaying, or NULL + LinkedList1Node relaying_list_node; // node in relay peer's relay_users + + // waiting for relay data + int waiting_relay; + LinkedList1Node waiting_relay_list_node; + + // retry timer + BTimer reset_timer; + + // relay server specific + int is_relay; + LinkedList1Node relay_list_node; + LinkedList1 relay_users; + + // binding state + int binding; + int binding_addrpos; + + // peers linked list node + LinkedList1Node list_node; +}; diff --git a/app/src/main/jni/badvpn/cmake/modules/COPYING-CMAKE-SCRIPTS b/app/src/main/jni/badvpn/cmake/modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/src/main/jni/badvpn/cmake/modules/FindGLIB2.cmake b/app/src/main/jni/badvpn/cmake/modules/FindGLIB2.cmake new file mode 100644 index 0000000..09fd98d --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/FindGLIB2.cmake @@ -0,0 +1,52 @@ +# - Try to find the GLIB2 libraries +# Once done this will define +# +# GLIB2_FOUND - system has glib2 +# GLIB2_INCLUDE_DIR - the glib2 include directory +# GLIB2_LIBRARIES - glib2 library + +# Copyright (c) 2008 Laurent Montel, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) + # Already in cache, be silent + set(GLIB2_FIND_QUIETLY TRUE) +endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) + +find_package(PkgConfig) +pkg_check_modules(PC_LibGLIB2 QUIET glib-2.0) + +find_path(GLIB2_MAIN_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_LibGLIB2_INCLUDEDIR} + PATH_SUFFIXES glib-2.0) + +find_library(GLIB2_LIBRARY + NAMES glib-2.0 + HINTS ${PC_LibGLIB2_LIBDIR} +) + +set(GLIB2_LIBRARIES ${GLIB2_LIBRARY}) + +# search the glibconfig.h include dir under the same root where the library is found +get_filename_component(glib2LibDir "${GLIB2_LIBRARIES}" PATH) + +find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h + PATH_SUFFIXES glib-2.0/include + HINTS ${PC_LibGLIB2_INCLUDEDIR} "${glib2LibDir}" ${CMAKE_SYSTEM_LIBRARY_PATH}) + +set(GLIB2_INCLUDE_DIR "${GLIB2_MAIN_INCLUDE_DIR}") + +# not sure if this include dir is optional or required +# for now it is optional +if(GLIB2_INTERNAL_INCLUDE_DIR) + set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} "${GLIB2_INTERNAL_INCLUDE_DIR}") +endif(GLIB2_INTERNAL_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR) + +mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES) diff --git a/app/src/main/jni/badvpn/cmake/modules/FindLibraryWithDebug.cmake b/app/src/main/jni/badvpn/cmake/modules/FindLibraryWithDebug.cmake new file mode 100644 index 0000000..58cd730 --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/FindLibraryWithDebug.cmake @@ -0,0 +1,113 @@ +# +# FIND_LIBRARY_WITH_DEBUG +# -> enhanced FIND_LIBRARY to allow the search for an +# optional debug library with a WIN32_DEBUG_POSTFIX similar +# to CMAKE_DEBUG_POSTFIX when creating a shared lib +# it has to be the second and third argument + +# Copyright (c) 2007, Christian Ehrlicher, +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +MACRO(FIND_LIBRARY_WITH_DEBUG var_name win32_dbg_postfix_name dgb_postfix libname) + + IF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + # no WIN32_DEBUG_POSTFIX -> simply pass all arguments to FIND_LIBRARY + FIND_LIBRARY(${var_name} + ${win32_dbg_postfix_name} + ${dgb_postfix} + ${libname} + ${ARGN} + ) + + ELSE(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + IF(NOT WIN32) + # on non-win32 we don't need to take care about WIN32_DEBUG_POSTFIX + + FIND_LIBRARY(${var_name} ${libname} ${ARGN}) + + ELSE(NOT WIN32) + + # 1. get all possible libnames + SET(args ${ARGN}) + SET(newargs "") + SET(libnames_release "") + SET(libnames_debug "") + + LIST(LENGTH args listCount) + + IF("${libname}" STREQUAL "NAMES") + SET(append_rest 0) + LIST(APPEND args " ") + + FOREACH(i RANGE ${listCount}) + LIST(GET args ${i} val) + + IF(append_rest) + LIST(APPEND newargs ${val}) + ELSE(append_rest) + IF("${val}" STREQUAL "PATHS") + LIST(APPEND newargs ${val}) + SET(append_rest 1) + ELSE("${val}" STREQUAL "PATHS") + LIST(APPEND libnames_release "${val}") + LIST(APPEND libnames_debug "${val}${dgb_postfix}") + ENDIF("${val}" STREQUAL "PATHS") + ENDIF(append_rest) + + ENDFOREACH(i) + + ELSE("${libname}" STREQUAL "NAMES") + + # just one name + LIST(APPEND libnames_release "${libname}") + LIST(APPEND libnames_debug "${libname}${dgb_postfix}") + + SET(newargs ${args}) + + ENDIF("${libname}" STREQUAL "NAMES") + + # search the release lib + FIND_LIBRARY(${var_name}_RELEASE + NAMES ${libnames_release} + ${newargs} + ) + + # search the debug lib + FIND_LIBRARY(${var_name}_DEBUG + NAMES ${libnames_debug} + ${newargs} + ) + + IF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + # both libs found + SET(${var_name} optimized ${${var_name}_RELEASE} + debug ${${var_name}_DEBUG}) + + ELSE(${var_name}_RELEASE AND ${var_name}_DEBUG) + + IF(${var_name}_RELEASE) + + # only release found + SET(${var_name} ${${var_name}_RELEASE}) + + ELSE(${var_name}_RELEASE) + + # only debug (or nothing) found + SET(${var_name} ${${var_name}_DEBUG}) + + ENDIF(${var_name}_RELEASE) + + ENDIF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + MARK_AS_ADVANCED(${var_name}_RELEASE) + MARK_AS_ADVANCED(${var_name}_DEBUG) + + ENDIF(NOT WIN32) + + ENDIF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + +ENDMACRO(FIND_LIBRARY_WITH_DEBUG) diff --git a/app/src/main/jni/badvpn/cmake/modules/FindNSPR.cmake b/app/src/main/jni/badvpn/cmake/modules/FindNSPR.cmake new file mode 100644 index 0000000..6e8fed9 --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/FindNSPR.cmake @@ -0,0 +1,57 @@ +# - Try to find the NSPR library +# Once done this will define +# +# NSPR_FOUND - system has the NSPR library +# NSPR_INCLUDE_DIRS - Include paths needed +# NSPR_LIBRARY_DIRS - Linker paths needed +# NSPR_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (NSPR_LIBRARIES) + set(NSPR_FIND_QUIETLY TRUE) +endif () + +set(NSPR_FOUND FALSE) + +if (WIN32) + find_path(NSPR_FIND_INCLUDE_DIR prerror.h) + + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLDS WIN32_DEBUG_POSTFIX d NAMES plds4 libplds4) + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLC WIN32_DEBUG_POSTFIX d NAMES plc4 libplc4) + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_NSPR WIN32_DEBUG_POSTFIX d NAMES nspr4 libnspr4) + + if (NSPR_FIND_INCLUDE_DIR AND NSPR_FIND_LIBRARIES_PLDS AND NSPR_FIND_LIBRARIES_PLC AND NSPR_FIND_LIBRARIES_NSPR) + set(NSPR_FOUND TRUE) + set(NSPR_INCLUDE_DIRS "${NSPR_FIND_INCLUDE_DIR}" CACHE STRING "NSPR include dirs") + set(NSPR_LIBRARY_DIRS "" CACHE STRING "NSPR library dirs") + set(NSPR_LIBRARIES "${NSPR_FIND_LIBRARIES_PLDS};${NSPR_FIND_LIBRARIES_PLC};${NSPR_FIND_LIBRARIES_NSPR}" CACHE STRING "NSPR libraries") + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(NSPR_PC nspr) + + if (NSPR_PC_FOUND) + set(NSPR_FOUND TRUE) + set(NSPR_INCLUDE_DIRS "${NSPR_PC_INCLUDE_DIRS}" CACHE STRING "NSPR include dirs") + set(NSPR_LIBRARY_DIRS "${NSPR_PC_LIBRARY_DIRS}" CACHE STRING "NSPR library dirs") + set(NSPR_LIBRARIES "${NSPR_PC_LIBRARIES}" CACHE STRING "NSPR libraries") + endif () +endif () + +if (NSPR_FOUND) + if (NOT NSPR_FIND_QUIETLY) + MESSAGE(STATUS "Found NSPR: ${NSPR_INCLUDE_DIRS} ${NSPR_LIBRARY_DIRS} ${NSPR_LIBRARIES}") + endif () +else () + if (NSPR_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find NSPR") + endif () +endif () + +mark_as_advanced(NSPR_INCLUDE_DIRS NSPR_LIBRARY_DIRS NSPR_LIBRARIES) diff --git a/app/src/main/jni/badvpn/cmake/modules/FindNSS.cmake b/app/src/main/jni/badvpn/cmake/modules/FindNSS.cmake new file mode 100644 index 0000000..17fd45a --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/FindNSS.cmake @@ -0,0 +1,57 @@ +# - Try to find the NSS library +# Once done this will define +# +# NSS_FOUND - system has the NSS library +# NSS_INCLUDE_DIRS - Include paths needed +# NSS_LIBRARY_DIRS - Linker paths needed +# NSS_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (NSS_LIBRARIES) + set(NSS_FIND_QUIETLY TRUE) +endif () + +set(NSS_FOUND FALSE) + +if (WIN32) + find_path(NSS_FIND_INCLUDE_DIR nss.h) + + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl3) + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SMIME WIN32_DEBUG_POSTFIX d NAMES smime3) + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_NSS WIN32_DEBUG_POSTFIX d NAMES nss3) + + if (NSS_FIND_INCLUDE_DIR AND NSS_FIND_LIBRARIES_SSL AND NSS_FIND_LIBRARIES_SMIME AND NSS_FIND_LIBRARIES_NSS) + set(NSS_FOUND TRUE) + set(NSS_INCLUDE_DIRS "${NSS_FIND_INCLUDE_DIR}" CACHE STRING "NSS include dirs") + set(NSS_LIBRARY_DIRS "" CACHE STRING "NSS library dirs") + set(NSS_LIBRARIES "${NSS_FIND_LIBRARIES_SSL};${NSS_FIND_LIBRARIES_SMIME};${NSS_FIND_LIBRARIES_NSS}" CACHE STRING "NSS libraries") + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(NSS_PC nss) + + if (NSS_PC_FOUND) + set(NSS_FOUND TRUE) + set(NSS_INCLUDE_DIRS "${NSS_PC_INCLUDE_DIRS}" CACHE STRING "NSS include dirs") + set(NSS_LIBRARY_DIRS "${NSS_PC_LIBRARY_DIRS}" CACHE STRING "NSS library dirs") + set(NSS_LIBRARIES "${NSS_PC_LIBRARIES}" CACHE STRING "NSS libraries") + endif () +endif () + +if (NSS_FOUND) + if (NOT NSS_FIND_QUIETLY) + MESSAGE(STATUS "Found NSS: ${NSS_INCLUDE_DIRS} ${NSS_LIBRARY_DIRS} ${NSS_LIBRARIES}") + endif () +else () + if (NSS_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find NSS") + endif () +endif () + +mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARY_DIRS NSS_LIBRARIES) diff --git a/app/src/main/jni/badvpn/cmake/modules/FindOpenSSL.cmake b/app/src/main/jni/badvpn/cmake/modules/FindOpenSSL.cmake new file mode 100644 index 0000000..4434e95 --- /dev/null +++ b/app/src/main/jni/badvpn/cmake/modules/FindOpenSSL.cmake @@ -0,0 +1,72 @@ +# - Try to find the OpenSSL library +# Once done this will define +# +# OpenSSL_FOUND - system has the OpenSSL library +# OpenSSL_INCLUDE_DIRS - Include paths needed +# OpenSSL_LIBRARY_DIRS - Linker paths needed +# OpenSSL_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (OpenSSL_LIBRARIES) + set(OpenSSL_FIND_QUIETLY TRUE) +endif () + +set(OpenSSL_FOUND FALSE) + +if (WIN32) + find_path(OpenSSL_FIND_INCLUDE_DIR openssl/ssl.h) + + if (OpenSSL_FIND_INCLUDE_DIR) + # look for libraries built with GCC + find_library(OpenSSL_FIND_LIBRARIES_SSL NAMES ssl) + find_library(OpenSSL_FIND_LIBRARIES_CRYPTO NAMES crypto) + + if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_CRYPTO) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_CRYPTO}" CACHE STRING "OpenSSL libraries") + else () + # look for libraries built with MSVC + FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl ssleay ssleay32 libssleay32 ssleay32MD) + FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_EAY WIN32_DEBUG_POSTFIX d NAMES eay libeay libeay32 libeay32MD) + + if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_EAY) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_EAY}" CACHE STRING "OpenSSL libraries") + endif () + endif () + + if (OpenSSL_FOUND) + set(OpenSSL_INCLUDE_DIRS "${OpenSSL_FIND_INCLUDE_DIR}" CACHE STRING "OpenSSL include dirs") + endif () + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(OpenSSL_PC openssl) + + if (OpenSSL_PC_FOUND) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_INCLUDE_DIRS "${OpenSSL_PC_INCLUDE_DIRS}" CACHE STRING "OpenSSL include dirs") + set(OpenSSL_LIBRARY_DIRS "${OpenSSL_PC_LIBRARY_DIRS}" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_PC_LIBRARIES}" CACHE STRING "OpenSSL libraries") + endif () +endif () + +if (OpenSSL_FOUND) + if (NOT OpenSSL_FIND_QUIETLY) + MESSAGE(STATUS "Found OpenSSL: ${OpenSSL_INCLUDE_DIRS} ${OpenSSL_LIBRARY_DIRS} ${OpenSSL_LIBRARIES}") + endif () +else () + if (OpenSSL_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find OpenSSL") + endif () +endif () + +mark_as_advanced(OpenSSL_INCLUDE_DIRS OpenSSL_LIBRARY_DIRS OpenSSL_LIBRARIES) diff --git a/app/src/main/jni/badvpn/compile-tun2sock.sh b/app/src/main/jni/badvpn/compile-tun2sock.sh new file mode 100644 index 0000000..fbc2388 --- /dev/null +++ b/app/src/main/jni/badvpn/compile-tun2sock.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# +# Compiles tun2socks for Linux. +# Intended as a convenience if you don't want to deal with CMake. + +# Input environment vars: +# SRCDIR - BadVPN source code +# CC - compiler +# CFLAGS - compiler compile flags +# LDFLAGS - compiler link flags +# ENDIAN - "little" or "big" +# KERNEL - "2.6" or "2.4", default "2.6" +# +# Puts object files and the executable in the working directory. +# + +if [[ -z $SRCDIR ]] || [[ ! -e $SRCDIR/CMakeLists.txt ]]; then + echo "SRCDIR is wrong" + exit 1 +fi + +if ! "${CC}" --version &>/dev/null; then + echo "CC is wrong" + exit 1 +fi + +if [[ $ENDIAN != "little" ]] && [[ $ENDIAN != "big" ]]; then + echo "ENDIAN is wrong" + exit 1 +fi + +if [[ -z $KERNEL ]]; then + KERNEL="2.6" +elif [[ $KERNEL != "2.6" ]] && [[ $KERNEL != "2.4" ]]; then + echo "KERNEL is wrong" + exit 1 +fi + +CFLAGS="${CFLAGS} -std=gnu99" +INCLUDES=( "-I${SRCDIR}" "-I${SRCDIR}/lwip/src/include/ipv4" "-I${SRCDIR}/lwip/src/include/ipv6" "-I${SRCDIR}/lwip/src/include" "-I${SRCDIR}/lwip/custom" ) +DEFS=( -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE ) + +[[ $KERNEL = "2.4" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_USE_SELFPIPE -DBADVPN_USE_POLL ) || DEFS=( "${DEFS[@]}" -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL ) + +[[ $ENDIAN = "little" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_LITTLE_ENDIAN ) || DEFS=( "${DEFS[@]}" -DBADVPN_BIG_ENDIAN ) + +SOURCES=" +base/BLog_syslog.c +system/BReactor_badvpn.c +system/BSignal.c +system/BConnection_unix.c +system/BTime.c +system/BUnixSignal.c +system/BNetwork.c +flow/StreamRecvInterface.c +flow/PacketRecvInterface.c +flow/PacketPassInterface.c +flow/StreamPassInterface.c +flow/SinglePacketBuffer.c +flow/BufferWriter.c +flow/PacketBuffer.c +flow/PacketStreamSender.c +flow/PacketPassConnector.c +flow/PacketProtoFlow.c +flow/PacketPassFairQueue.c +flow/PacketProtoEncoder.c +flow/PacketProtoDecoder.c +socksclient/BSocksClient.c +tuntap/BTap.c +lwip/src/core/timers.c +lwip/src/core/udp.c +lwip/src/core/memp.c +lwip/src/core/init.c +lwip/src/core/pbuf.c +lwip/src/core/tcp.c +lwip/src/core/tcp_out.c +lwip/src/core/netif.c +lwip/src/core/def.c +lwip/src/core/mem.c +lwip/src/core/tcp_in.c +lwip/src/core/stats.c +lwip/src/core/inet_chksum.c +lwip/src/core/ipv4/icmp.c +lwip/src/core/ipv4/ip4.c +lwip/src/core/ipv4/ip4_addr.c +lwip/src/core/ipv4/ip_frag.c +lwip/src/core/ipv6/ip6.c +lwip/src/core/ipv6/nd6.c +lwip/src/core/ipv6/icmp6.c +lwip/src/core/ipv6/ip6_addr.c +lwip/src/core/ipv6/ip6_frag.c +lwip/custom/sys.c +tun2socks/tun2socks.c +base/DebugObject.c +base/BLog.c +base/BPending.c +flowextra/PacketPassInactivityMonitor.c +tun2socks/SocksUdpGwClient.c +udpgw_client/UdpGwClient.c +" + +set -e +set -x + +OBJS=() +for f in $SOURCES; do + obj=$(basename "${f}").o + "${CC}" -c ${CFLAGS} "${INCLUDES[@]}" "${DEFS[@]}" "${SRCDIR}/${f}" -o "${obj}" + OBJS=( "${OBJS[@]}" "${obj}" ) +done + +"${CC}" ${LDFLAGS} "${OBJS[@]}" -o tun2socks -lrt diff --git a/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.c b/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.c new file mode 100644 index 0000000..70ee6ad --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.c @@ -0,0 +1,340 @@ +/** + * @file BDHCPClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_OVERHEAD (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static const struct sock_filter dhcp_sock_filter[] = { + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 9), // A <- IP protocol + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPV4_PROTOCOL_UDP, 0, 3), // IP protocol = UDP ? + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22), // A <- UDP destination port + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1), // UDP destination port = DHCP client ? + BPF_STMT(BPF_RET + BPF_K, 65535), // return all + BPF_STMT(BPF_RET + BPF_K, 0) // ignore +}; + +static void dgram_handler (BDHCPClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "packet socket error"); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDHCPCLIENT_EVENT_ERROR)); + return; +} + +static void dhcp_handler (BDHCPClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + + switch (event) { + case BDHCPCLIENTCORE_EVENT_UP: + ASSERT(!o->up) + o->up = 1; + o->handler(o->user, BDHCPCLIENT_EVENT_UP); + return; + + case BDHCPCLIENTCORE_EVENT_DOWN: + ASSERT(o->up) + o->up = 0; + o->handler(o->user, BDHCPCLIENT_EVENT_DOWN); + return; + + default: + ASSERT(0); + } +} + +static void dhcp_func_getsendermac (BDHCPClient *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + + BAddr remote_addr; + BIPAddr local_addr; + if (!BDatagram_GetLastReceiveAddrs(&o->dgram, &remote_addr, &local_addr)) { + BLog(BLOG_ERROR, "BDatagram_GetLastReceiveAddrs failed"); + goto fail; + } + + if (remote_addr.type != BADDR_TYPE_PACKET) { + BLog(BLOG_ERROR, "address type invalid"); + goto fail; + } + + if (remote_addr.packet.header_type != BADDR_PACKET_HEADER_TYPE_ETHERNET) { + BLog(BLOG_ERROR, "address header type invalid"); + goto fail; + } + + memcpy(out_mac, remote_addr.packet.phys_addr, 6); + return; + +fail: + memset(out_mac, 0, 6); +} + +int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // get interface information + uint8_t if_mac[6]; + int if_mtu; + int if_index; + if (!badvpn_get_iface_info(ifname, if_mac, &if_mtu, &if_index)) { + BLog(BLOG_ERROR, "failed to get interface information"); + goto fail0; + } + + BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d", + if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index); + + if (if_mtu < IPUDP_OVERHEAD) { + BLog(BLOG_ERROR, "MTU is too small for UDP/IP !?!"); + goto fail0; + } + + int dhcp_mtu = if_mtu - IPUDP_OVERHEAD; + + // init dgram + if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) { + BLog(BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // set socket filter + { + struct sock_filter filter[sizeof(dhcp_sock_filter) / sizeof(dhcp_sock_filter[0])]; + memcpy(filter, dhcp_sock_filter, sizeof(filter)); + struct sock_fprog fprog = { + .len = sizeof(filter) / sizeof(filter[0]), + .filter = filter + }; + if (setsockopt(BDatagram_GetFd(&o->dgram), SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) { + BLog(BLOG_NOTICE, "not using socket filter"); + } + } + + // bind dgram + BAddr bind_addr; + BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac); + if (!BDatagram_Bind(&o->dgram, bind_addr)) { + BLog(BLOG_ERROR, "BDatagram_Bind failed"); + goto fail1; + } + + // set dgram send addresses + BAddr dest_addr; + uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac); + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr); + + // init dgram interfaces + BDatagram_SendAsync_Init(&o->dgram, if_mtu); + BDatagram_RecvAsync_Init(&o->dgram, if_mtu); + + // init sending + + // init copier + PacketCopier_Init(&o->send_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor)); + + // init encoder + DHCPIpUdpEncoder_Init(&o->send_encoder, PacketCopier_GetOutput(&o->send_copier), BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->send_buffer, DHCPIpUdpEncoder_GetOutput(&o->send_encoder), BDatagram_SendAsync_GetIf(&o->dgram), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init receiving + + // init copier + PacketCopier_Init(&o->recv_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor)); + + // init decoder + DHCPIpUdpDecoder_Init(&o->recv_decoder, PacketCopier_GetInput(&o->recv_copier), BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->recv_buffer, BDatagram_RecvAsync_GetIf(&o->dgram), DHCPIpUdpDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail3; + } + + // init options + struct BDHCPClientCore_opts core_opts; + core_opts.hostname = opts.hostname; + core_opts.vendorclassid = opts.vendorclassid; + core_opts.clientid = opts.clientid; + core_opts.clientid_len = opts.clientid_len; + + // auto-generate clientid from MAC if requested + uint8_t mac_cid[7]; + if (opts.auto_clientid) { + mac_cid[0] = DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET; + memcpy(mac_cid + 1, if_mac, 6); + core_opts.clientid = mac_cid; + core_opts.clientid_len = sizeof(mac_cid); + } + + // init dhcp + if (!BDHCPClientCore_Init(&o->dhcp, PacketCopier_GetInput(&o->send_copier), PacketCopier_GetOutput(&o->recv_copier), if_mac, core_opts, o->reactor, random2, o, + (BDHCPClientCore_func_getsendermac)dhcp_func_getsendermac, + (BDHCPClientCore_handler)dhcp_handler + )) { + BLog(BLOG_ERROR, "BDHCPClientCore_Init failed"); + goto fail4; + } + + // set not up + o->up = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail4: + SinglePacketBuffer_Free(&o->recv_buffer); +fail3: + DHCPIpUdpDecoder_Free(&o->recv_decoder); + PacketCopier_Free(&o->recv_copier); + SinglePacketBuffer_Free(&o->send_buffer); +fail2: + DHCPIpUdpEncoder_Free(&o->send_encoder); + PacketCopier_Free(&o->send_copier); + BDatagram_RecvAsync_Free(&o->dgram); + BDatagram_SendAsync_Free(&o->dgram); +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void BDHCPClient_Free (BDHCPClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free dhcp + BDHCPClientCore_Free(&o->dhcp); + + // free receiving + SinglePacketBuffer_Free(&o->recv_buffer); + DHCPIpUdpDecoder_Free(&o->recv_decoder); + PacketCopier_Free(&o->recv_copier); + + // free sending + SinglePacketBuffer_Free(&o->send_buffer); + DHCPIpUdpEncoder_Free(&o->send_encoder); + PacketCopier_Free(&o->send_copier); + + // free dgram interfaces + BDatagram_RecvAsync_Free(&o->dgram); + BDatagram_SendAsync_Free(&o->dgram); + + // free dgram + BDatagram_Free(&o->dgram); +} + +int BDHCPClient_IsUp (BDHCPClient *o) +{ + DebugObject_Access(&o->d_obj); + + return o->up; +} + +void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetClientIP(&o->dhcp, out_ip); +} + +void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetClientMask(&o->dhcp, out_mask); +} + +int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return BDHCPClientCore_GetRouter(&o->dhcp, out_router); +} + +int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return BDHCPClientCore_GetDNS(&o->dhcp, out_dns_servers, max_dns_servers); +} + +void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetServerMAC(&o->dhcp, out_mac); +} diff --git a/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.h b/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.h new file mode 100644 index 0000000..c0da0c4 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/BDHCPClient.h @@ -0,0 +1,87 @@ +/** + * @file BDHCPClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client. + */ + +#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENT_H +#define BADVPN_DHCPCLIENT_BDHCPCLIENT_H + +#include +#include +#include +#include +#include +#include +#include + +#define BDHCPCLIENT_EVENT_UP 1 +#define BDHCPCLIENT_EVENT_DOWN 2 +#define BDHCPCLIENT_EVENT_ERROR 3 + +#define BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS + +typedef void (*BDHCPClient_handler) (void *user, int event); + +typedef struct { + BReactor *reactor; + BDatagram dgram; + BDHCPClient_handler handler; + void *user; + PacketCopier send_copier; + DHCPIpUdpEncoder send_encoder; + SinglePacketBuffer send_buffer; + SinglePacketBuffer recv_buffer; + DHCPIpUdpDecoder recv_decoder; + PacketCopier recv_copier; + BDHCPClientCore dhcp; + int up; + DebugError d_err; + DebugObject d_obj; +} BDHCPClient; + +struct BDHCPClient_opts { + const char *hostname; + const char *vendorclassid; + const uint8_t *clientid; + size_t clientid_len; + int auto_clientid; +}; + +int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user); +void BDHCPClient_Free (BDHCPClient *o); +int BDHCPClient_IsUp (BDHCPClient *o); +void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip); +void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask); +int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router); +int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers); +void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac); + +#endif diff --git a/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.c b/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.c new file mode 100644 index 0000000..5a605e4 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.c @@ -0,0 +1,860 @@ +/** + * @file BDHCPClientCore.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define RESET_TIMEOUT 4000 +#define REQUEST_TIMEOUT 3000 +#define RENEW_REQUEST_TIMEOUT 20000 +#define MAX_REQUESTS 4 +#define RENEW_TIMEOUT(lease) ((btime_t)500 * (lease)) +#define XID_REUSE_MAX 8 + +#define LEASE_TIMEOUT(lease) ((btime_t)1000 * (lease) - RENEW_TIMEOUT(lease)) + +#define STATE_RESETTING 1 +#define STATE_SENT_DISCOVER 2 +#define STATE_SENT_REQUEST 3 +#define STATE_FINISHED 4 +#define STATE_RENEWING 5 + +#define IP_UDP_HEADERS_SIZE 28 + +static void report_up (BDHCPClientCore *o) +{ + o->handler(o->user, BDHCPCLIENTCORE_EVENT_UP); + return; +} + +static void report_down (BDHCPClientCore *o) +{ + o->handler(o->user, BDHCPCLIENTCORE_EVENT_DOWN); + return; +} + +static void send_message ( + BDHCPClientCore *o, + int type, + uint32_t xid, + int have_requested_ip_address, uint32_t requested_ip_address, + int have_dhcp_server_identifier, uint32_t dhcp_server_identifier +) +{ + ASSERT(type == DHCP_MESSAGE_TYPE_DISCOVER || type == DHCP_MESSAGE_TYPE_REQUEST) + + if (o->sending) { + BLog(BLOG_ERROR, "already sending"); + return; + } + + // write header + struct dhcp_header header; + memset(&header, 0, sizeof(header)); + header.op = hton8(DHCP_OP_BOOTREQUEST); + header.htype = hton8(DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET); + header.hlen = hton8(6); + header.xid = xid; + header.secs = hton16(0); + memcpy(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr)); + header.magic = hton32(DHCP_MAGIC); + memcpy(o->send_buf, &header, sizeof(header)); + + // write options + + char *out = o->send_buf + sizeof(header); + struct dhcp_option_header oh; + + // DHCP message type + { + oh.type = hton8(DHCP_OPTION_DHCP_MESSAGE_TYPE); + oh.len = hton8(sizeof(struct dhcp_option_dhcp_message_type)); + struct dhcp_option_dhcp_message_type opt; + opt.type = hton8(type); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (have_requested_ip_address) { + // requested IP address + oh.type = hton8(DHCP_OPTION_REQUESTED_IP_ADDRESS); + oh.len = hton8(sizeof(struct dhcp_option_addr)); + struct dhcp_option_addr opt; + opt.addr = requested_ip_address; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (have_dhcp_server_identifier) { + // DHCP server identifier + oh.type = hton8(DHCP_OPTION_DHCP_SERVER_IDENTIFIER); + oh.len = hton8(sizeof(struct dhcp_option_dhcp_server_identifier)); + struct dhcp_option_dhcp_server_identifier opt; + opt.id = dhcp_server_identifier; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + // maximum message size + { + oh.type = hton8(DHCP_OPTION_MAXIMUM_MESSAGE_SIZE); + oh.len = hton8(sizeof(struct dhcp_option_maximum_message_size)); + struct dhcp_option_maximum_message_size opt; + opt.size = hton16(IP_UDP_HEADERS_SIZE + PacketRecvInterface_GetMTU(o->recv_if)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + // parameter request list + { + oh.type = hton8(DHCP_OPTION_PARAMETER_REQUEST_LIST); + oh.len = hton8(4); + uint8_t opt[4]; + opt[0] = DHCP_OPTION_SUBNET_MASK; + opt[1] = DHCP_OPTION_ROUTER; + opt[2] = DHCP_OPTION_DOMAIN_NAME_SERVER; + opt[3] = DHCP_OPTION_IP_ADDRESS_LEASE_TIME; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (o->hostname) { + // host name + oh.type = hton8(DHCP_OPTION_HOST_NAME); + oh.len = hton8(strlen(o->hostname)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->hostname, strlen(o->hostname)); + out += sizeof(oh) + strlen(o->hostname); + } + + if (o->vendorclassid) { + // vendor class identifier + oh.type = hton8(DHCP_OPTION_VENDOR_CLASS_IDENTIFIER); + oh.len = hton8(strlen(o->vendorclassid)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->vendorclassid, strlen(o->vendorclassid)); + out += sizeof(oh) + strlen(o->vendorclassid); + } + + if (o->clientid) { + // client identifier + oh.type = hton8(DHCP_OPTION_CLIENT_IDENTIFIER); + oh.len = hton8(o->clientid_len); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->clientid, o->clientid_len); + out += sizeof(oh) + o->clientid_len; + } + + // end option + uint8_t end = 0xFF; + memcpy(out, &end, sizeof(end)); + out += sizeof(end); + + // send it + PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)o->send_buf, out - o->send_buf); + o->sending = 1; +} + +static void send_handler_done (BDHCPClientCore *o) +{ + ASSERT(o->sending) + DebugObject_Access(&o->d_obj); + + o->sending = 0; +} + +static void recv_handler_done (BDHCPClientCore *o, int data_len) +{ + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + // receive more packets + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf); + + if (o->state == STATE_RESETTING) { + return; + } + + // check header + + if (data_len < sizeof(struct dhcp_header)) { + return; + } + + struct dhcp_header header; + memcpy(&header, o->recv_buf, sizeof(header)); + + if (ntoh8(header.op) != DHCP_OP_BOOTREPLY) { + return; + } + + if (ntoh8(header.htype) != DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET) { + return; + } + + if (ntoh8(header.hlen) != 6) { + return; + } + + if (header.xid != o->xid) { + return; + } + + if (memcmp(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr))) { + return; + } + + if (ntoh32(header.magic) != DHCP_MAGIC) { + return; + } + + // parse and check options + + uint8_t *pos = (uint8_t *)o->recv_buf + sizeof(header); + int len = data_len - sizeof(header); + + int have_end = 0; + + int dhcp_message_type = -1; + + int have_dhcp_server_identifier = 0; + uint32_t dhcp_server_identifier = 0; // to remove warning + + int have_ip_address_lease_time = 0; + uint32_t ip_address_lease_time = 0; // to remove warning + + int have_subnet_mask = 0; + uint32_t subnet_mask = 0; // to remove warning + + int have_router = 0; + uint32_t router = 0; // to remove warning + + int domain_name_servers_count = 0; + uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS]; + + while (len > 0) { + // padding option ? + if (*pos == 0) { + pos++; + len--; + continue; + } + + if (have_end) { + return; + } + + // end option ? + if (*pos == 0xff) { + pos++; + len--; + have_end = 1; + continue; + } + + // check option header + if (len < sizeof(struct dhcp_option_header)) { + return; + } + struct dhcp_option_header opt; + memcpy(&opt, pos, sizeof(opt)); + pos += sizeof(opt); + len -= sizeof(opt); + int opt_type = ntoh8(opt.type); + int opt_len = ntoh8(opt.len); + + // check option payload + if (opt_len > len) { + return; + } + uint8_t *optval = pos; + pos += opt_len; + len -= opt_len; + + switch (opt_type) { + case DHCP_OPTION_DHCP_MESSAGE_TYPE: { + if (opt_len != sizeof(struct dhcp_option_dhcp_message_type)) { + return; + } + struct dhcp_option_dhcp_message_type val; + memcpy(&val, optval, sizeof(val)); + + dhcp_message_type = ntoh8(val.type); + } break; + + case DHCP_OPTION_DHCP_SERVER_IDENTIFIER: { + if (opt_len != sizeof(struct dhcp_option_dhcp_server_identifier)) { + return; + } + struct dhcp_option_dhcp_server_identifier val; + memcpy(&val, optval, sizeof(val)); + + dhcp_server_identifier = val.id; + have_dhcp_server_identifier = 1; + } break; + + case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: { + if (opt_len != sizeof(struct dhcp_option_time)) { + return; + } + struct dhcp_option_time val; + memcpy(&val, optval, sizeof(val)); + + ip_address_lease_time = ntoh32(val.time); + have_ip_address_lease_time = 1; + } break; + + case DHCP_OPTION_SUBNET_MASK: { + if (opt_len != sizeof(struct dhcp_option_addr)) { + return; + } + struct dhcp_option_addr val; + memcpy(&val, optval, sizeof(val)); + + subnet_mask = val.addr; + have_subnet_mask = 1; + } break; + + case DHCP_OPTION_ROUTER: { + if (opt_len != sizeof(struct dhcp_option_addr)) { + return; + } + struct dhcp_option_addr val; + memcpy(&val, optval, sizeof(val)); + + router = val.addr; + have_router = 1; + } break; + + case DHCP_OPTION_DOMAIN_NAME_SERVER: { + if (opt_len % sizeof(struct dhcp_option_addr)) { + return; + } + + int num_servers = opt_len / sizeof(struct dhcp_option_addr); + + int i; + for (i = 0; i < num_servers && i < BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS; i++) { + struct dhcp_option_addr addr; + memcpy(&addr, optval + i * sizeof(addr), sizeof(addr)); + domain_name_servers[i] = addr.addr; + } + + domain_name_servers_count = i; + } break; + } + } + + if (!have_end) { + return; + } + + if (dhcp_message_type == -1) { + return; + } + + if (dhcp_message_type != DHCP_MESSAGE_TYPE_OFFER && dhcp_message_type != DHCP_MESSAGE_TYPE_ACK && dhcp_message_type != DHCP_MESSAGE_TYPE_NAK) { + return; + } + + if (!have_dhcp_server_identifier) { + return; + } + + if (dhcp_message_type == DHCP_MESSAGE_TYPE_NAK) { + if (o->state != STATE_SENT_REQUEST && o->state != STATE_FINISHED && o->state != STATE_RENEWING) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + if (o->state == STATE_SENT_REQUEST) { + BLog(BLOG_INFO, "received NAK (in sent request)"); + + // stop request timer + BReactor_RemoveTimer(o->reactor, &o->request_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + } + else if (o->state == STATE_FINISHED) { + BLog(BLOG_INFO, "received NAK (in finished)"); + + // stop renew timer + BReactor_RemoveTimer(o->reactor, &o->renew_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + + // report to user + report_down(o); + return; + } + else { // STATE_RENEWING + BLog(BLOG_INFO, "received NAK (in renewing)"); + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // stop lease timer + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + + // report to user + report_down(o); + return; + } + + return; + } + + if (ntoh32(header.yiaddr) == 0) { + return; + } + + if (!have_ip_address_lease_time) { + return; + } + + if (!have_subnet_mask) { + return; + } + + if (o->state == STATE_SENT_DISCOVER && dhcp_message_type == DHCP_MESSAGE_TYPE_OFFER) { + BLog(BLOG_INFO, "received OFFER"); + + // remember offer + o->offered.yiaddr = header.yiaddr; + o->offered.dhcp_server_identifier = dhcp_server_identifier; + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier); + + // stop reset timer + BReactor_RemoveTimer(o->reactor, &o->reset_timer); + + // start request timer + BReactor_SetTimer(o->reactor, &o->request_timer); + + // set state + o->state = STATE_SENT_REQUEST; + + // set request count + o->request_count = 1; + } + else if (o->state == STATE_SENT_REQUEST && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) { + if (header.yiaddr != o->offered.yiaddr) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + BLog(BLOG_INFO, "received ACK (in sent request)"); + + // remember stuff + o->acked.ip_address_lease_time = ip_address_lease_time; + o->acked.subnet_mask = subnet_mask; + o->acked.have_router = have_router; + if (have_router) { + o->acked.router = router; + } + o->acked.domain_name_servers_count = domain_name_servers_count; + memcpy(o->acked.domain_name_servers, domain_name_servers, domain_name_servers_count * sizeof(uint32_t)); + o->func_getsendermac(o->user, o->acked.server_mac); + + // stop request timer + BReactor_RemoveTimer(o->reactor, &o->request_timer); + + // start renew timer + BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_FINISHED; + + // report to user + report_up(o); + return; + } + else if (o->state == STATE_RENEWING && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) { + if (header.yiaddr != o->offered.yiaddr) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + // TODO: check parameters? + + BLog(BLOG_INFO, "received ACK (in renewing)"); + + // remember stuff + o->acked.ip_address_lease_time = ip_address_lease_time; + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // stop lease timer + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + + // start renew timer + BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_FINISHED; + } +} + +static void start_process (BDHCPClientCore *o, int force_new_xid) +{ + if (force_new_xid || o->xid_reuse_counter == XID_REUSE_MAX) { + // generate xid + if (!BRandom2_GenBytes(o->random2, &o->xid, sizeof(o->xid))) { + BLog(BLOG_ERROR, "BRandom2_GenBytes failed"); + o->xid = UINT32_C(3416960072); + } + + // reset counter + o->xid_reuse_counter = 0; + } + + // increment counter + o->xid_reuse_counter++; + + // send discover + send_message(o, DHCP_MESSAGE_TYPE_DISCOVER, o->xid, 0, 0, 0, 0); + + // set timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_SENT_DISCOVER; +} + +static void reset_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RESETTING || o->state == STATE_SENT_DISCOVER) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "reset timer"); + + start_process(o, (o->state == STATE_RESETTING)); +} + +static void request_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_SENT_REQUEST) + ASSERT(o->request_count >= 1) + ASSERT(o->request_count <= MAX_REQUESTS) + DebugObject_Access(&o->d_obj); + + // if we have sent enough requests, start again + if (o->request_count == MAX_REQUESTS) { + BLog(BLOG_INFO, "request timer, aborting"); + + start_process(o, 0); + return; + } + + BLog(BLOG_INFO, "request timer, retrying"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier); + + // start request timer + BReactor_SetTimer(o->reactor, &o->request_timer); + + // increment request count + o->request_count++; +} + +static void renew_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_FINISHED) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "renew timer"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0); + + // start renew request timer + BReactor_SetTimer(o->reactor, &o->renew_request_timer); + + // start lease timer + BReactor_SetTimerAfter(o->reactor, &o->lease_timer, LEASE_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_RENEWING; +} + +static void renew_request_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "renew request timer"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0); + + // start renew request timer + BReactor_SetTimer(o->reactor, &o->renew_request_timer); +} + +static void lease_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "lease timer"); + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // start again now + start_process(o, 1); + + // report to user + report_down(o); + return; +} + +static bsize_t maybe_len (const char *str) +{ + return bsize_fromsize(str ? strlen(str) : 0); +} + +int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, + uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, + BRandom2 *random2, void *user, + BDHCPClientCore_func_getsendermac func_getsendermac, + BDHCPClientCore_handler handler) +{ + ASSERT(PacketPassInterface_GetMTU(send_if) == PacketRecvInterface_GetMTU(recv_if)) + ASSERT(PacketPassInterface_GetMTU(send_if) >= 576 - IP_UDP_HEADERS_SIZE) + ASSERT(func_getsendermac) + ASSERT(handler) + + // init arguments + o->send_if = send_if; + o->recv_if = recv_if; + memcpy(o->client_mac_addr, client_mac_addr, sizeof(o->client_mac_addr)); + o->reactor = reactor; + o->random2 = random2; + o->user = user; + o->func_getsendermac = func_getsendermac; + o->handler = handler; + + o->hostname = NULL; + o->vendorclassid = NULL; + o->clientid = NULL; + o->clientid_len = 0; + + // copy options + if (opts.hostname && !(o->hostname = strdup(opts.hostname))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail0; + } + if (opts.vendorclassid && !(o->vendorclassid = strdup(opts.vendorclassid))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail0; + } + if (opts.clientid) { + if (!(o->clientid = BAlloc(opts.clientid_len))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + memcpy(o->clientid, opts.clientid, opts.clientid_len); + o->clientid_len = opts.clientid_len; + } + + // make sure options aren't too long + bsize_t opts_size = bsize_add(maybe_len(o->hostname), bsize_add(maybe_len(o->vendorclassid), bsize_fromsize(o->clientid_len))); + if (opts_size.is_overflow || opts_size.value > 100) { + BLog(BLOG_ERROR, "options too long together"); + goto fail0; + } + if (o->hostname && strlen(o->hostname) > 255) { + BLog(BLOG_ERROR, "hostname too long"); + goto fail0; + } + if (o->vendorclassid && strlen(o->vendorclassid) > 255) { + BLog(BLOG_ERROR, "vendorclassid too long"); + goto fail0; + } + if (o->clientid && o->clientid_len > 255) { + BLog(BLOG_ERROR, "clientid too long"); + goto fail0; + } + + // allocate buffers + if (!(o->send_buf = BAlloc(PacketPassInterface_GetMTU(send_if)))) { + BLog(BLOG_ERROR, "BAlloc send buf failed"); + goto fail0; + } + if (!(o->recv_buf = BAlloc(PacketRecvInterface_GetMTU(recv_if)))) { + BLog(BLOG_ERROR, "BAlloc recv buf failed"); + goto fail1; + } + + // init send interface + PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_handler_done, o); + + // init receive interface + PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_handler_done, o); + + // set not sending + o->sending = 0; + + // init timers + BTimer_Init(&o->reset_timer, RESET_TIMEOUT, (BTimer_handler)reset_timer_handler, o); + BTimer_Init(&o->request_timer, REQUEST_TIMEOUT, (BTimer_handler)request_timer_handler, o); + BTimer_Init(&o->renew_timer, 0, (BTimer_handler)renew_timer_handler, o); + BTimer_Init(&o->renew_request_timer, RENEW_REQUEST_TIMEOUT, (BTimer_handler)renew_request_timer_handler, o); + BTimer_Init(&o->lease_timer, 0, (BTimer_handler)lease_timer_handler, o); + + // start receving + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf); + + // start + start_process(o, 1); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + BFree(o->send_buf); +fail0: + BFree(o->clientid); + free(o->vendorclassid); + free(o->hostname); + return 0; +} + +void BDHCPClientCore_Free (BDHCPClientCore *o) +{ + DebugObject_Free(&o->d_obj); + + // free timers + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + BReactor_RemoveTimer(o->reactor, &o->renew_timer); + BReactor_RemoveTimer(o->reactor, &o->request_timer); + BReactor_RemoveTimer(o->reactor, &o->reset_timer); + + // free buffers + BFree(o->recv_buf); + BFree(o->send_buf); + + // free options + BFree(o->clientid); + free(o->vendorclassid); + free(o->hostname); +} + +void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + *out_ip = o->offered.yiaddr; +} + +void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + *out_mask = o->acked.subnet_mask; +} + +int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + if (!o->acked.have_router) { + return 0; + } + + *out_router = o->acked.router; + return 1; +} + +int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + int num_return = bmin_int(o->acked.domain_name_servers_count, max_dns_servers); + + memcpy(out_dns_servers, o->acked.domain_name_servers, num_return * sizeof(uint32_t)); + return num_return; +} + +void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + + memcpy(out_mac, o->acked.server_mac, 6); +} diff --git a/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.h b/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.h new file mode 100644 index 0000000..98cda36 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/BDHCPClientCore.h @@ -0,0 +1,114 @@ +/** + * @file BDHCPClientCore.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client, excluding system-dependent details. + */ + +#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H +#define BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H + +#include +#include + +#include +#include +#include +#include +#include + +#define BDHCPCLIENTCORE_EVENT_UP 1 +#define BDHCPCLIENTCORE_EVENT_DOWN 2 + +#define BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS 16 + +typedef void (*BDHCPClientCore_func_getsendermac) (void *user, uint8_t *out_mac); +typedef void (*BDHCPClientCore_handler) (void *user, int event); + +struct BDHCPClientCore_opts { + const char *hostname; + const char *vendorclassid; + const uint8_t *clientid; + size_t clientid_len; +}; + +typedef struct { + PacketPassInterface *send_if; + PacketRecvInterface *recv_if; + uint8_t client_mac_addr[6]; + BReactor *reactor; + BRandom2 *random2; + void *user; + BDHCPClientCore_func_getsendermac func_getsendermac; + BDHCPClientCore_handler handler; + char *hostname; + char *vendorclassid; + uint8_t *clientid; + size_t clientid_len; + char *send_buf; + char *recv_buf; + int sending; + BTimer reset_timer; + BTimer request_timer; + BTimer renew_timer; + BTimer renew_request_timer; + BTimer lease_timer; + int state; + int request_count; + uint32_t xid; + int xid_reuse_counter; + struct { + uint32_t yiaddr; + uint32_t dhcp_server_identifier; + } offered; + struct { + uint32_t ip_address_lease_time; + uint32_t subnet_mask; + int have_router; + uint32_t router; + int domain_name_servers_count; + uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS]; + uint8_t server_mac[6]; + } acked; + DebugObject d_obj; +} BDHCPClientCore; + +int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, + uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, + BRandom2 *random2, void *user, + BDHCPClientCore_func_getsendermac func_getsendermac, + BDHCPClientCore_handler handler); +void BDHCPClientCore_Free (BDHCPClientCore *o); +void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip); +void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask); +int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router); +int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers); +void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac); + +#endif diff --git a/app/src/main/jni/badvpn/dhcpclient/CMakeLists.txt b/app/src/main/jni/badvpn/dhcpclient/CMakeLists.txt new file mode 100644 index 0000000..6d38928 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(dhcpclientcore + BDHCPClientCore.c +) +target_link_libraries(dhcpclientcore system flow flowextra badvpn_random) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_library(dhcpclient + BDHCPClient.c + DHCPIpUdpEncoder.c + DHCPIpUdpDecoder.c + ) + target_link_libraries(dhcpclient system flow dhcpclientcore) +endif () diff --git a/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.c b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.c new file mode 100644 index 0000000..1d1c7a7 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.c @@ -0,0 +1,137 @@ +/** + * @file DHCPIpUdpDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static void input_handler_send (DHCPIpUdpDecoder *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + struct ipv4_header iph; + uint8_t *pl; + int pl_len; + + if (!ipv4_check(data, data_len, &iph, &pl, &pl_len)) { + goto fail; + } + + if (ntoh8(iph.protocol) != IPV4_PROTOCOL_UDP) { + goto fail; + } + + if (pl_len < sizeof(struct udp_header)) { + goto fail; + } + struct udp_header udph; + memcpy(&udph, pl, sizeof(udph)); + + if (ntoh16(udph.source_port) != DHCP_SERVER_PORT) { + goto fail; + } + + if (ntoh16(udph.dest_port) != DHCP_CLIENT_PORT) { + goto fail; + } + + int udph_length = ntoh16(udph.length); + if (udph_length < sizeof(udph)) { + goto fail; + } + if (udph_length > data_len - (pl - data)) { + goto fail; + } + + if (ntoh16(udph.checksum) != 0) { + uint16_t checksum_in_packet = udph.checksum; + udph.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udph, pl + sizeof(udph), udph_length - sizeof(udph), iph.source_address, iph.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + } + + // pass payload to output + PacketPassInterface_Sender_Send(o->output, pl + sizeof(udph), udph_length - sizeof(udph)); + + return; + +fail: + PacketPassInterface_Done(&o->input); +} + +static void output_handler_done (DHCPIpUdpDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Done(&o->input); +} + +void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) <= INT_MAX - IPUDP_HEADER_SIZE) + + // init arguments + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init input + PacketPassInterface_Init(&o->input, IPUDP_HEADER_SIZE + PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.h b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.h new file mode 100644 index 0000000..ce92138 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpDecoder.h @@ -0,0 +1,49 @@ +/** + * @file DHCPIpUdpDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H +#define BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H + +#include + +#include +#include + +typedef struct { + PacketPassInterface *output; + PacketPassInterface input; + uint8_t *data; + DebugObject d_obj; +} DHCPIpUdpDecoder; + +void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg); +void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o); +PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o); + +#endif diff --git a/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.c b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.c new file mode 100644 index 0000000..da6645c --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.c @@ -0,0 +1,119 @@ +/** + * @file DHCPIpUdpEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static void output_handler_recv (DHCPIpUdpEncoder *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + // remember output packet + o->data = data; + + // receive payload + PacketRecvInterface_Receiver_Recv(o->input, o->data + IPUDP_HEADER_SIZE); +} + +static void input_handler_done (DHCPIpUdpEncoder *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // build IP header + struct ipv4_header iph; + iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph)); + iph.ds = hton8(0); + iph.total_length = hton16(IPUDP_HEADER_SIZE + data_len); + iph.identification = hton16(0); + iph.flags3_fragmentoffset13 = hton16(0); + iph.ttl = hton8(64); + iph.protocol = hton8(IPV4_PROTOCOL_UDP); + iph.checksum = hton16(0); + iph.source_address = hton32(0x00000000); + iph.destination_address = hton32(0xFFFFFFFF); + iph.checksum = ipv4_checksum(&iph, NULL, 0); + + // write UDP header + struct udp_header udph; + udph.source_port = hton16(DHCP_CLIENT_PORT); + udph.dest_port = hton16(DHCP_SERVER_PORT); + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_checksum(&udph, o->data + IPUDP_HEADER_SIZE, data_len, iph.source_address, iph.destination_address); + + // write header + memcpy(o->data, &iph, sizeof(iph)); + memcpy(o->data + sizeof(iph), &udph, sizeof(udph)); + + // finish packet + PacketRecvInterface_Done(&o->output, IPUDP_HEADER_SIZE + data_len); +} + +void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - IPUDP_HEADER_SIZE) + + // init arguments + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketRecvInterface_Init(&o->output, IPUDP_HEADER_SIZE + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.h b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.h new file mode 100644 index 0000000..d3e0ac0 --- /dev/null +++ b/app/src/main/jni/badvpn/dhcpclient/DHCPIpUdpEncoder.h @@ -0,0 +1,49 @@ +/** + * @file DHCPIpUdpEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H +#define BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H + +#include + +#include +#include + +typedef struct { + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *data; + DebugObject d_obj; +} DHCPIpUdpEncoder; + +void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg); +void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o); +PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o); + +#endif diff --git a/app/src/main/jni/badvpn/dostest/CMakeLists.txt b/app/src/main/jni/badvpn/dostest/CMakeLists.txt new file mode 100644 index 0000000..8d3b742 --- /dev/null +++ b/app/src/main/jni/badvpn/dostest/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(dostest-server + dostest-server.c + StreamBuffer.c +) +target_link_libraries(dostest-server base system) + +add_executable(dostest-attacker + dostest-attacker.c +) +target_link_libraries(dostest-attacker base system) diff --git a/app/src/main/jni/badvpn/dostest/StreamBuffer.c b/app/src/main/jni/badvpn/dostest/StreamBuffer.c new file mode 100644 index 0000000..d439127 --- /dev/null +++ b/app/src/main/jni/badvpn/dostest/StreamBuffer.c @@ -0,0 +1,147 @@ +/** + * @file StreamBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "StreamBuffer.h" + +// called when receive operation is complete +static void input_handler_done (void *vo, int data_len) +{ + StreamBuffer *o = (StreamBuffer *)vo; + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - (o->buf_start + o->buf_used)) + + // remember if buffer was empty + int was_empty = (o->buf_used == 0); + + // increment buf_used by the amount that was received + o->buf_used += data_len; + + // start another receive operation unless buffer is full + if (o->buf_used < o->buf_size - o->buf_start) { + int end = o->buf_start + o->buf_used; + StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, o->buf_size - end); + } + else if (o->buf_used < o->buf_size) { + // wrap around + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_start); + } + + // if buffer was empty before, start send operation + if (was_empty) { + StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, o->buf_used); + } +} + +// called when send operation is complete +static void output_handler_done (void *vo, int data_len) +{ + StreamBuffer *o = (StreamBuffer *)vo; + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_used) + ASSERT(data_len <= o->buf_size - o->buf_start) + + // remember if buffer was full + int was_full = (o->buf_used == o->buf_size); + + // increment buf_start and decrement buf_used by the + // amount that was sent + o->buf_start += data_len; + o->buf_used -= data_len; + + // wrap around buf_start + if (o->buf_start == o->buf_size) { + o->buf_start = 0; + } + + // start receive operation if buffer was full + if (was_full) { + int end; + int avail; + if (o->buf_used >= o->buf_size - o->buf_start) { + end = o->buf_used - (o->buf_size - o->buf_start); + avail = o->buf_start - end; + } else { + end = o->buf_start + o->buf_used; + avail = o->buf_size - end; + } + StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, avail); + } + + // start another receive send unless buffer is empty + if (o->buf_used > 0) { + int to_send = bmin_int(o->buf_used, o->buf_size - o->buf_start); + StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, to_send); + } +} + +int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output) +{ + ASSERT(buf_size > 0) + ASSERT(input) + ASSERT(output) + + // remember arguments + o->buf_size = buf_size; + o->input = input; + o->output = output; + + // allocate buffer memory + o->buf = (uint8_t *)BAllocSize(bsize_fromint(o->buf_size)); + if (!o->buf) { + goto fail0; + } + + // set initial buffer state + o->buf_start = 0; + o->buf_used = 0; + + // set receive and send done callbacks + StreamRecvInterface_Receiver_Init(o->input, input_handler_done, o); + StreamPassInterface_Sender_Init(o->output, output_handler_done, o); + + // start receive operation + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void StreamBuffer_Free (StreamBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer memory + BFree(o->buf); +} diff --git a/app/src/main/jni/badvpn/dostest/StreamBuffer.h b/app/src/main/jni/badvpn/dostest/StreamBuffer.h new file mode 100644 index 0000000..dd441f5 --- /dev/null +++ b/app/src/main/jni/badvpn/dostest/StreamBuffer.h @@ -0,0 +1,70 @@ +/** + * @file StreamBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STREAMBUFFER_H +#define BADVPN_STREAMBUFFER_H + +#include +#include +#include +#include + +/** + * Buffer object which reads data from a \link StreamRecvInterface and writes + * it to a \link StreamPassInterface. + */ +typedef struct { + int buf_size; + StreamRecvInterface *input; + StreamPassInterface *output; + uint8_t *buf; + int buf_start; + int buf_used; + DebugObject d_obj; +} StreamBuffer; + +/** + * Initializes the buffer object. + * + * @param o object to initialize + * @param buf_size size of the buffer. Must be >0. + * @param input input interface + * @param outout output interface + * @return 1 on success, 0 on failure + */ +int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output) WARN_UNUSED; + +/** + * Frees the buffer object. + * + * @param o object to free + */ +void StreamBuffer_Free (StreamBuffer *o); + +#endif diff --git a/app/src/main/jni/badvpn/dostest/dostest-attacker.c b/app/src/main/jni/badvpn/dostest/dostest-attacker.c new file mode 100644 index 0000000..723dadd --- /dev/null +++ b/app/src/main/jni/badvpn/dostest/dostest-attacker.c @@ -0,0 +1,512 @@ +/** + * @file dostest-attacker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PROGRAM_NAME "dostest-attacker" + +// connection structure +struct connection { + int connected; + BConnector connector; + BConnection con; + StreamRecvInterface *recv_if; + uint8_t buf[512]; + LinkedList1Node connections_list_node; +}; + +// command-line options +static struct { + int help; + int version; + char *connect_addr; + int max_connections; + int max_connecting; + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; +} options; + +// connect address +static BAddr connect_addr; + +// reactor +static BReactor reactor; + +// connections +static LinkedList1 connections_list; +static int num_connections; +static int num_connecting; + +// timer for scheduling creation of more connections +static BTimer make_connections_timer; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static int connection_new (void); +static void connection_free (struct connection *conn); +static void connection_logfunc (struct connection *conn); +static void connection_log (struct connection *conn, int level, const char *fmt, ...); +static void connection_connector_handler (struct connection *conn, int is_error); +static void connection_connection_handler (struct connection *conn, int event); +static void connection_recv_handler_done (struct connection *conn, int data_len); +static void make_connections_timer_handler (void *unused); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // init loger + BLog_InitStderr(); + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // init connections list + LinkedList1_Init(&connections_list); + num_connections = 0; + num_connecting = 0; + + // init make connections timer + BTimer_Init(&make_connections_timer, 0, make_connections_timer_handler, NULL); + BReactor_SetTimer(&reactor, &make_connections_timer); + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&reactor); + + // free connections + while (!LinkedList1_IsEmpty(&connections_list)) { + struct connection *conn = UPPER_OBJECT(LinkedList1_GetFirst(&connections_list), struct connection, connections_list_node); + connection_free(conn); + } + // free make connections timer + BReactor_RemoveTimer(&reactor, &make_connections_timer); + // free signal + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&reactor); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " --connect-addr \n" + " --max-connections \n" + " --max-connecting \n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.connect_addr = NULL; + options.max_connections = -1; + options.max_connecting = -1; + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--connect-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.connect_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--max-connections")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connections = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-connecting")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connecting = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.connect_addr) { + fprintf(stderr, "--connect-addr missing\n"); + return 0; + } + + if (options.max_connections == -1) { + fprintf(stderr, "--max-connections missing\n"); + return 0; + } + + if (options.max_connecting == -1) { + fprintf(stderr, "--max-connecting missing\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen address + if (!BAddr_Parse(&connect_addr, options.connect_addr, NULL, 0)) { + BLog(BLOG_ERROR, "connect addr: BAddr_Parse failed"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&reactor, 1); +} + +int connection_new (void) +{ + // allocate structure + struct connection *conn = (struct connection *)malloc(sizeof(*conn)); + if (!conn) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set not connected + conn->connected = 0; + + // init connector + if (!BConnector_Init(&conn->connector, connect_addr, &reactor, conn, (BConnector_handler)connection_connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail1; + } + + // add to connections list + LinkedList1_Append(&connections_list, &conn->connections_list_node); + num_connections++; + num_connecting++; + + return 1; + +fail1: + free(conn); +fail0: + return 0; +} + +void connection_free (struct connection *conn) +{ + // remove from connections list + LinkedList1_Remove(&connections_list, &conn->connections_list_node); + num_connections--; + if (!conn->connected) { + num_connecting--; + } + + if (conn->connected) { + // free receive interface + BConnection_RecvAsync_Free(&conn->con); + + // free connection + BConnection_Free(&conn->con); + } + + // free connector + BConnector_Free(&conn->connector); + + // free structure + free(conn); +} + +void connection_logfunc (struct connection *conn) +{ + BLog_Append("%d connection (%p): ", num_connecting, (void *)conn); +} + +void connection_log (struct connection *conn, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)connection_logfunc, conn, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void connection_connector_handler (struct connection *conn, int is_error) +{ + ASSERT(!conn->connected) + + // check for connection error + if (is_error) { + connection_log(conn, BLOG_INFO, "failed to connect"); + goto fail0; + } + + // init connection from connector + if (!BConnection_Init(&conn->con, BConnection_source_connector(&conn->connector), &reactor, conn, (BConnection_handler)connection_connection_handler)) { + connection_log(conn, BLOG_INFO, "BConnection_Init failed"); + goto fail0; + } + + // init receive interface + BConnection_RecvAsync_Init(&conn->con); + conn->recv_if = BConnection_RecvAsync_GetIf(&conn->con); + StreamRecvInterface_Receiver_Init(conn->recv_if, (StreamRecvInterface_handler_done)connection_recv_handler_done, conn); + + // start receiving + StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf)); + + // no longer connecting + conn->connected = 1; + num_connecting--; + + connection_log(conn, BLOG_INFO, "connected"); + + // schedule making connections (because of connecting limit) + BReactor_SetTimer(&reactor, &make_connections_timer); + return; + +fail0: + // free connection + connection_free(conn); + + // schedule making connections + BReactor_SetTimer(&reactor, &make_connections_timer); +} + +void connection_connection_handler (struct connection *conn, int event) +{ + ASSERT(conn->connected) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + connection_log(conn, BLOG_INFO, "connection closed"); + } else { + connection_log(conn, BLOG_INFO, "connection error"); + } + + // free connection + connection_free(conn); + + // schedule making connections + BReactor_SetTimer(&reactor, &make_connections_timer); +} + +void connection_recv_handler_done (struct connection *conn, int data_len) +{ + ASSERT(conn->connected) + + // receive more + StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf)); + + connection_log(conn, BLOG_INFO, "received %d bytes", data_len); +} + +void make_connections_timer_handler (void *unused) +{ + int make_num = bmin_int(options.max_connections - num_connections, options.max_connecting - num_connecting); + + if (make_num <= 0) { + return; + } + + BLog(BLOG_INFO, "making %d connections", make_num); + + for (int i = 0; i < make_num; i++) { + if (!connection_new()) { + // can happen if fd limit is reached + BLog(BLOG_ERROR, "failed to make connection, waiting"); + BReactor_SetTimerAfter(&reactor, &make_connections_timer, 10); + return; + } + } +} diff --git a/app/src/main/jni/badvpn/dostest/dostest-server.c b/app/src/main/jni/badvpn/dostest/dostest-server.c new file mode 100644 index 0000000..7447591 --- /dev/null +++ b/app/src/main/jni/badvpn/dostest/dostest-server.c @@ -0,0 +1,567 @@ +/** + * @file dostest-server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#ifdef BADVPN_LINUX +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "StreamBuffer.h" + +#include + +#define PROGRAM_NAME "dostest-server" + +#ifdef BADVPN_LINUX +#ifndef SO_DOSDEF_PREPARE +#define SO_DOSDEF_PREPARE 48 +#endif +#ifndef SO_DOSDEF_ACTIVATE +#define SO_DOSDEF_ACTIVATE 49 +#endif +#endif + +#define BUF_SIZE 1024 + +// client structure +struct client { + BConnection con; + BAddr addr; + StreamBuffer buf; + BTimer disconnect_timer; + LinkedList1Node clients_list_node; +}; + +// command-line options +static struct { + int help; + int version; + char *listen_addr; + int max_clients; + int disconnect_time; + int defense_prepare_clients; + int defense_activate_clients; + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; +} options; + +// listen address +static BAddr listen_addr; + +// reactor +static BReactor ss; + +// listener +static BListener listener; + +// clients +static LinkedList1 clients_list; +static int num_clients; + +// defense status +static int defense_prepare; +static int defense_activate; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static void listener_handler (void *unused); +static void client_free (struct client *client); +static void client_logfunc (struct client *client); +static void client_log (struct client *client, int level, const char *fmt, ...); +static void client_disconnect_timer_handler (struct client *client); +static void client_connection_handler (struct client *client, int event); +static void update_defense (void); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // init loger + BLog_InitStderr(); + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // initialize listener + if (!BListener_Init(&listener, listen_addr, &ss, NULL, listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail3; + } + + // init clients list + LinkedList1_Init(&clients_list); + num_clients = 0; + + // clear defense state + defense_prepare = 0; + defense_activate = 0; + + // update defense + update_defense(); + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + while (!LinkedList1_IsEmpty(&clients_list)) { + struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node); + client_free(client); + } + // free listener + BListener_Free(&listener); +fail3: + // free signal + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&ss); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " --listen-addr \n" + " --max-clients \n" + " --disconnect-time \n" + " [--defense-prepare-clients ]\n" + " [--defense-activate-clients ]\n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.listen_addr = NULL; + options.max_clients = -1; + options.disconnect_time = -1; + options.defense_prepare_clients = -1; + options.defense_activate_clients = -1; + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.listen_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--disconnect-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.disconnect_time = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--defense-prepare-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.defense_prepare_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--defense-activate-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.defense_activate_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.listen_addr) { + fprintf(stderr, "--listen-addr missing\n"); + return 0; + } + + if (options.max_clients == -1) { + fprintf(stderr, "--max-clients missing\n"); + return 0; + } + + if (options.disconnect_time == -1) { + fprintf(stderr, "--disconnect-time missing\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen address + if (!BAddr_Parse(&listen_addr, options.listen_addr, NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void listener_handler (void *unused) +{ + if (num_clients == options.max_clients) { + BLog(BLOG_ERROR, "maximum number of clients reached"); + goto fail0; + } + + // allocate structure + struct client *client = (struct client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // accept client + if (!BConnection_Init(&client->con, BConnection_source_listener(&listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init connection interfaces + BConnection_RecvAsync_Init(&client->con); + BConnection_SendAsync_Init(&client->con); + StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->con); + StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->con); + + // init stream buffer (to loop received data back to the client) + if (!StreamBuffer_Init(&client->buf, BUF_SIZE, recv_if, send_if)) { + BLog(BLOG_ERROR, "StreamBuffer_Init failed"); + goto fail2; + } + + // init disconnect timer + BTimer_Init(&client->disconnect_timer, options.disconnect_time, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // insert to clients list + LinkedList1_Append(&clients_list, &client->clients_list_node); + num_clients++; + + client_log(client, BLOG_INFO, "connected"); + BLog(BLOG_NOTICE, "%d clients", num_clients); + + // update defense + update_defense(); + return; + +fail2: + BConnection_SendAsync_Free(&client->con); + BConnection_RecvAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_free (struct client *client) +{ + // remove from clients list + LinkedList1_Remove(&clients_list, &client->clients_list_node); + num_clients--; + + // free disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free stream buffer + StreamBuffer_Free(&client->buf); + + // free connection interfaces + BConnection_SendAsync_Free(&client->con); + BConnection_RecvAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free structure + free(client); + + BLog(BLOG_NOTICE, "%d clients", num_clients); + + // update defense + update_defense(); +} + +void client_logfunc (struct client *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client (%s): ", addr); +} + +void client_log (struct client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client *client) +{ + client_log(client, BLOG_INFO, "timed out, disconnecting"); + + // free client + client_free(client); +} + +void client_connection_handler (struct client *client, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "client closed"); + } else { + client_log(client, BLOG_INFO, "client error"); + } + + // free client + client_free(client); +} + +void update_defense (void) +{ +#ifdef BADVPN_LINUX + if (options.defense_prepare_clients != -1) { + int val = num_clients >= options.defense_prepare_clients; + int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_PREPARE, &val, sizeof(val)); + if (res < 0) { + BLog(BLOG_ERROR, "failed to %s defense preparation", (val ? "enable" : "disable")); + } else { + if (!defense_prepare && val) { + BLog(BLOG_NOTICE, "defense preparation enabled"); + } + else if (defense_prepare && !val) { + BLog(BLOG_NOTICE, "defense preparation disabled"); + } + } + defense_prepare = val; + } + + if (options.defense_activate_clients != -1) { + int val = num_clients >= options.defense_activate_clients; + int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_ACTIVATE, &val, sizeof(val)); + if (res < 0) { + BLog(BLOG_ERROR, "failed to %s defense activation", (val ? "enable" : "disable")); + } else { + if (!defense_activate && val) { + BLog(BLOG_NOTICE, "defense activation enabled"); + } + else if (defense_activate && !val) { + BLog(BLOG_NOTICE, "defense activation disabled"); + } + } + defense_activate = val; + } +#endif +} diff --git a/app/src/main/jni/badvpn/examples/CMakeLists.txt b/app/src/main/jni/badvpn/examples/CMakeLists.txt new file mode 100644 index 0000000..27dbeaa --- /dev/null +++ b/app/src/main/jni/badvpn/examples/CMakeLists.txt @@ -0,0 +1,97 @@ +if (NOT EMSCRIPTEN) + add_executable(btimer_example btimer_example.c) + target_link_libraries(btimer_example system) +endif () + +if (BUILDING_PREDICATE) + add_executable(predicate_test predicate_test.c) + target_link_libraries(predicate_test predicate) +endif () + +if (NOT EMSCRIPTEN) + add_executable(fairqueue_test fairqueue_test.c) + target_link_libraries(fairqueue_test system flow) +endif () + +add_executable(indexedlist_test indexedlist_test.c) + +if (BUILDING_SECURITY) + add_executable(fairqueue_test2 fairqueue_test2.c) + target_link_libraries(fairqueue_test2 system flow security) + + add_executable(bavl_test bavl_test.c) + target_link_libraries(bavl_test security) + + add_executable(savl_test savl_test.c) + target_link_libraries(savl_test security) + + add_executable(bencryption_bench bencryption_bench.c) + target_link_libraries(bencryption_bench system security) +endif () + +if (BUILD_NCD) + add_executable(ncd_tokenizer_test ncd_tokenizer_test.c) + target_link_libraries(ncd_tokenizer_test ncdtokenizer) + + add_executable(ncd_parser_test ncd_parser_test.c) + target_link_libraries(ncd_parser_test ncdconfigparser ncdvalgenerator ncdsugar) + + add_executable(ncd_value_parser_test ncd_value_parser_test.c) + target_link_libraries(ncd_value_parser_test ncdvalparser ncdvalgenerator) + + if (NOT EMSCRIPTEN) + add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c) + target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor) + endif () + + add_executable(ncdval_test ncdval_test.c) + target_link_libraries(ncdval_test ncdval) + + add_executable(ncdvalcons_test ncdvalcons_test.c) + target_link_libraries(ncdvalcons_test ncdvalcons ncdvalgenerator) +endif () + +if (BUILDING_UDEVMONITOR) + add_executable(ncdudevmonitor_test ncdudevmonitor_test.c) + target_link_libraries(ncdudevmonitor_test udevmonitor) + + add_executable(ncdudevmanager_test ncdudevmanager_test.c) + target_link_libraries(ncdudevmanager_test udevmonitor) +endif () + +if (NOT WIN32 AND NOT EMSCRIPTEN) + add_executable(bprocess_example bprocess_example.c) + target_link_libraries(bprocess_example system) + + add_executable(stdin_input stdin_input.c) + target_link_libraries(stdin_input system flow flowextra) +endif () + +if (BUILDING_DHCPCLIENT) + add_executable(dhcpclient_test dhcpclient_test.c) + target_link_libraries(dhcpclient_test dhcpclient) +endif () + +if (BUILDING_ARPPROBE) + add_executable(arpprobe_test arpprobe_test.c) + target_link_libraries(arpprobe_test arpprobe) +endif () + +add_executable(substring_test substring_test.c) + +if (NOT WIN32) + add_executable(ipaddr6_test ipaddr6_test.c) + add_executable(parse_number_test parse_number_test.c) +endif () + +if (BUILDING_RANDOM) + add_executable(brandom2_test brandom2_test.c) + target_link_libraries(brandom2_test badvpn_random) +endif () + +add_executable(cavl_test cavl_test.c) + +if (EMSCRIPTEN) + add_executable(emscripten_test emscripten_test.c) + target_link_libraries(emscripten_test system) +endif () diff --git a/app/src/main/jni/badvpn/examples/FastPacketSource.h b/app/src/main/jni/badvpn/examples/FastPacketSource.h new file mode 100644 index 0000000..e13e2f2 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/FastPacketSource.h @@ -0,0 +1,79 @@ +/** + * @file FastPacketSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FASTPACKETSOURCE_H +#define _FASTPACKETSOURCE_H + +#include +#include + +#include +#include +#include + +typedef struct { + PacketPassInterface *output; + int psize; + uint8_t *data; + int data_len; + DebugObject d_obj; +} FastPacketSource; + +static void _FastPacketSource_output_handler_done (FastPacketSource *s) +{ + DebugObject_Access(&s->d_obj); + + PacketPassInterface_Sender_Send(s->output, s->data, s->data_len); +} + +static void FastPacketSource_Init (FastPacketSource *s, PacketPassInterface *output, uint8_t *data, int data_len, BPendingGroup *pg) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= PacketPassInterface_GetMTU(output)); + + // init arguments + s->output = output; + s->data = data; + s->data_len = data_len; + + // init output + PacketPassInterface_Sender_Init(s->output, (PacketPassInterface_handler_done)_FastPacketSource_output_handler_done, s); + + // schedule send + PacketPassInterface_Sender_Send(s->output, s->data, s->data_len); + + DebugObject_Init(&s->d_obj); +} + +static void FastPacketSource_Free (FastPacketSource *s) +{ + DebugObject_Free(&s->d_obj); +} + +#endif diff --git a/app/src/main/jni/badvpn/examples/RandomPacketSink.h b/app/src/main/jni/badvpn/examples/RandomPacketSink.h new file mode 100644 index 0000000..cbadf78 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/RandomPacketSink.h @@ -0,0 +1,116 @@ +/** + * @file RandomPacketSink.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RANDOMPACKETSINK_H +#define _RANDOMPACKETSINK_H + +#include + +#include +#include +#include +#include +#include + +typedef struct { + BReactor *reactor; + PacketPassInterface input; + BTimer timer; + DebugObject d_obj; +} RandomPacketSink; + +static void _RandomPacketSink_input_handler_send (RandomPacketSink *s, uint8_t *data, int data_len) +{ + DebugObject_Access(&s->d_obj); + + printf("sink: send '"); + size_t res = fwrite(data, data_len, 1, stdout); + B_USE(res) + + uint8_t r; + BRandom_randomize(&r, sizeof(r)); + if (r&(uint8_t)1) { + printf("' accepting\n"); + PacketPassInterface_Done(&s->input); + } else { + printf("' delaying\n"); + BReactor_SetTimer(s->reactor, &s->timer); + } +} + +static void _RandomPacketSink_input_handler_requestcancel (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + printf("sink: cancelled\n"); + BReactor_RemoveTimer(s->reactor, &s->timer); + PacketPassInterface_Done(&s->input); +} + +static void _RandomPacketSink_timer_handler (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + PacketPassInterface_Done(&s->input); +} + +static void RandomPacketSink_Init (RandomPacketSink *s, BReactor *reactor, int mtu, int ms) +{ + // init arguments + s->reactor = reactor; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_RandomPacketSink_input_handler_send, s, BReactor_PendingGroup(reactor)); + PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_RandomPacketSink_input_handler_requestcancel); + + // init timer + BTimer_Init(&s->timer, ms, (BTimer_handler)_RandomPacketSink_timer_handler, s); + + DebugObject_Init(&s->d_obj); +} + +static void RandomPacketSink_Free (RandomPacketSink *s) +{ + DebugObject_Free(&s->d_obj); + + // free timer + BReactor_RemoveTimer(s->reactor, &s->timer); + + // free input + PacketPassInterface_Free(&s->input); +} + +static PacketPassInterface * RandomPacketSink_GetInput (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + return &s->input; +} + +#endif diff --git a/app/src/main/jni/badvpn/examples/TimerPacketSink.h b/app/src/main/jni/badvpn/examples/TimerPacketSink.h new file mode 100644 index 0000000..e1e8217 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/TimerPacketSink.h @@ -0,0 +1,97 @@ +/** + * @file TimerPacketSink.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TIMERPACKETSINK_H +#define _TIMERPACKETSINK_H + +#include + +#include +#include +#include + +typedef struct { + BReactor *reactor; + PacketPassInterface input; + BTimer timer; +} TimerPacketSink; + +static void _TimerPacketSink_input_handler_send (TimerPacketSink *s, uint8_t *data, int data_len) +{ + printf("sink: send '"); + size_t res = fwrite(data, data_len, 1, stdout); + B_USE(res) + printf("'\n"); + + BReactor_SetTimer(s->reactor, &s->timer); +} + +static void _TimerPacketSink_input_handler_requestcancel (TimerPacketSink *s) +{ + printf("sink: cancelled\n"); + + BReactor_RemoveTimer(s->reactor, &s->timer); + PacketPassInterface_Done(&s->input); +} + +static void _TimerPacketSink_timer_handler (TimerPacketSink *s) +{ + printf("sink: done\n"); + + PacketPassInterface_Done(&s->input); +} + +static void TimerPacketSink_Init (TimerPacketSink *s, BReactor *reactor, int mtu, int ms) +{ + // init arguments + s->reactor = reactor; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_TimerPacketSink_input_handler_send, s, BReactor_PendingGroup(s->reactor)); + PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_TimerPacketSink_input_handler_requestcancel); + + // init timer + BTimer_Init(&s->timer, ms, (BTimer_handler)_TimerPacketSink_timer_handler, s); +} + +static void TimerPacketSink_Free (TimerPacketSink *s) +{ + // free timer + BReactor_RemoveTimer(s->reactor, &s->timer); + + // free input + PacketPassInterface_Free(&s->input); +} + +static PacketPassInterface * TimerPacketSink_GetInput (TimerPacketSink *s) +{ + return &s->input; +} + +#endif diff --git a/app/src/main/jni/badvpn/examples/arpprobe_test.c b/app/src/main/jni/badvpn/examples/arpprobe_test.c new file mode 100644 index 0000000..d075f52 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/arpprobe_test.c @@ -0,0 +1,131 @@ +/** + * @file arpprobe_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BArpProbe arpprobe; + +static void signal_handler (void *user); +static void arpprobe_handler (void *unused, int event); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + goto fail0; + } + + char *ifname = argv[1]; + uint32_t addr = inet_addr(argv[2]); + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + if (!BArpProbe_Init(&arpprobe, ifname, addr, &reactor, NULL, arpprobe_handler)) { + DEBUG("BArpProbe_Init failed"); + goto fail3; + } + + BReactor_Exec(&reactor); + + BArpProbe_Free(&arpprobe); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 0); +} + +void arpprobe_handler (void *unused, int event) +{ + switch (event) { + case BARPPROBE_EVENT_EXIST: { + printf("ARPPROBE: exist\n"); + } break; + + case BARPPROBE_EVENT_NOEXIST: { + printf("ARPPROBE: noexist\n"); + } break; + + case BARPPROBE_EVENT_ERROR: { + printf("ARPPROBE: error\n"); + + // exit reactor + BReactor_Quit(&reactor, 0); + } break; + + default: + ASSERT(0); + } +} diff --git a/app/src/main/jni/badvpn/examples/bavl_test.c b/app/src/main/jni/badvpn/examples/bavl_test.c new file mode 100644 index 0000000..c30ae6f --- /dev/null +++ b/app/src/main/jni/badvpn/examples/bavl_test.c @@ -0,0 +1,129 @@ +/** + * @file bavl_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +struct mynode { + int used; + int num; + BAVLNode avl_node; +}; + +static int int_comparator (void *user, int *val1, int *val2) +{ + return B_COMPARE(*val1, *val2); +} + +static void verify (BAVL *tree) +{ + printf("Verifying...\n"); + BAVL_Verify(tree); +} + +int main (int argc, char **argv) +{ + int num_nodes; + int num_random_delete; + + if (argc != 3 || (num_nodes = atoi(argv[1])) <= 0 || (num_random_delete = atoi(argv[2])) < 0) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + return 1; + } + + struct mynode *nodes = (struct mynode *)BAllocArray(num_nodes, sizeof(*nodes)); + ASSERT_FORCE(nodes) + + int *values_ins = (int *)BAllocArray(num_nodes, sizeof(int)); + ASSERT_FORCE(values_ins) + + int *values = (int *)BAllocArray(num_random_delete, sizeof(int)); + ASSERT_FORCE(values) + + BAVL avl; + BAVL_Init(&avl, OFFSET_DIFF(struct mynode, num, avl_node), (BAVL_comparator)int_comparator, NULL); + verify(&avl); + + printf("Inserting random values...\n"); + int inserted = 0; + BRandom_randomize((uint8_t *)values_ins, num_nodes * sizeof(int)); + for (int i = 0; i < num_nodes; i++) { + nodes[i].num = values_ins[i]; + if (BAVL_Insert(&avl, &nodes[i].avl_node, NULL)) { + nodes[i].used = 1; + inserted++; + } else { + nodes[i].used = 0; + printf("Insert collision!\n"); + } + } + printf("Inserted %d entries\n", inserted); + verify(&avl); + + printf("Removing random entries...\n"); + int removed1 = 0; + BRandom_randomize((uint8_t *)values, num_random_delete * sizeof(int)); + for (int i = 0; i < num_random_delete; i++) { + int index = (((unsigned int *)values)[i] % num_nodes); + struct mynode *node = nodes + index; + if (node->used) { + BAVL_Remove(&avl, &node->avl_node); + node->used = 0; + removed1++; + } + } + printf("Removed %d entries\n", removed1); + verify(&avl); + + printf("Removing remaining...\n"); + int removed2 = 0; + while (!BAVL_IsEmpty(&avl)) { + struct mynode *node = UPPER_OBJECT(BAVL_GetFirst(&avl), struct mynode, avl_node); + ASSERT_FORCE(node->used) + BAVL_Remove(&avl, &node->avl_node); + node->used = 0; + removed2++; + } + printf("Removed %d entries\n", removed2); + ASSERT_FORCE(BAVL_IsEmpty(&avl)) + ASSERT_FORCE(removed1 + removed2 == inserted) + verify(&avl); + + BFree(nodes); + BFree(values_ins); + BFree(values); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/bencryption_bench.c b/app/src/main/jni/badvpn/examples/bencryption_bench.c new file mode 100644 index 0000000..c842bf2 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/bencryption_bench.c @@ -0,0 +1,146 @@ +/** + * @file bencryption_bench.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void usage (char *name) +{ + printf( + "Usage: %s \n" + " is one of (blowfish, aes).\n", + name + ); + + exit(1); +} + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 5) { + usage(argv[0]); + } + + char *mode_str = argv[1]; + char *cipher_str = argv[2]; + + int mode; + int cipher = 0; // silence warning + int num_blocks = atoi(argv[3]); + int num_ops = atoi(argv[4]); + + if (!strcmp(mode_str, "enc")) { + mode = BENCRYPTION_MODE_ENCRYPT; + } + else if (!strcmp(mode_str, "dec")) { + mode = BENCRYPTION_MODE_DECRYPT; + } + else { + usage(argv[0]); + } + + if (!strcmp(cipher_str, "blowfish")) { + cipher = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(cipher_str, "aes")) { + cipher = BENCRYPTION_CIPHER_AES; + } + else { + usage(argv[0]); + } + + if (num_blocks < 0 || num_ops < 0) { + usage(argv[0]); + } + + int key_size = BEncryption_cipher_key_size(cipher); + int block_size = BEncryption_cipher_block_size(cipher); + + uint8_t key[BENCRYPTION_MAX_KEY_SIZE]; + BRandom_randomize(key, key_size); + + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + BRandom_randomize(iv, block_size); + + if (num_blocks > INT_MAX / block_size) { + printf("too much"); + goto fail0; + } + int unit_size = num_blocks * block_size; + + printf("unit size %d\n", unit_size); + + uint8_t *buf1 = (uint8_t *)BAlloc(unit_size); + if (!buf1) { + printf("BAlloc failed"); + goto fail0; + } + + uint8_t *buf2 = (uint8_t *)BAlloc(unit_size); + if (!buf2) { + printf("BAlloc failed"); + goto fail1; + } + + BEncryption enc; + BEncryption_Init(&enc, mode, cipher, key); + + uint8_t *in = buf1; + uint8_t *out = buf2; + BRandom_randomize(in, unit_size); + + for (int i = 0; i < num_ops; i++) { + BEncryption_Encrypt(&enc, in, out, unit_size, iv); + + uint8_t *t = in; + in = out; + out = t; + } + + BEncryption_Free(&enc); + BFree(buf2); +fail1: + BFree(buf1); +fail0: + DebugObjectGlobal_Finish(); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/bprocess_example.c b/app/src/main/jni/badvpn/examples/bprocess_example.c new file mode 100644 index 0000000..0ece996 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/bprocess_example.c @@ -0,0 +1,140 @@ +/** + * @file bprocess_example.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BUnixSignal unixsignal; +BProcessManager manager; +BProcess process; + +static void unixsignal_handler (void *user, int signo); +static void process_handler (void *user, int normally, uint8_t normally_exit_status); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + int ret = 1; + + if (argc < 2) { + printf("Usage: %s [argument ...]\n", argv[0]); + goto fail0; + } + + char *program = argv[1]; + + // init time + BTime_Init(); + + // init logger + BLog_InitStdout(); + + // init reactor (event loop) + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + // choose signals to catch + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + + // init BUnixSignal for catching signals + if (!BUnixSignal_Init(&unixsignal, &reactor, set, unixsignal_handler, NULL)) { + DEBUG("BUnixSignal_Init failed"); + goto fail2; + } + + // init process manager + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + char **p_argv = argv + 1; + + // map fds 0, 1, 2 in child to fds 0, 1, 2 in parent + int fds[] = { 0, 1, 2, -1 }; + int fds_map[] = { 0, 1, 2 }; + + // start child process + if (!BProcess_InitWithFds(&process, &manager, process_handler, NULL, program, p_argv, NULL, fds, fds_map)) { + DEBUG("BProcess_Init failed"); + goto fail4; + } + + // enter event loop + ret = BReactor_Exec(&reactor); + + BProcess_Free(&process); +fail4: + BProcessManager_Free(&manager); +fail3: + BUnixSignal_Free(&unixsignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void unixsignal_handler (void *user, int signo) +{ + DEBUG("received %s, terminating child", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // send SIGTERM to child + BProcess_Terminate(&process); +} + +void process_handler (void *user, int normally, uint8_t normally_exit_status) +{ + DEBUG("process terminated"); + + int ret = (normally ? normally_exit_status : 1); + + // return from event loop + BReactor_Quit(&reactor, ret); +} diff --git a/app/src/main/jni/badvpn/examples/brandom2_test.c b/app/src/main/jni/badvpn/examples/brandom2_test.c new file mode 100644 index 0000000..539735c --- /dev/null +++ b/app/src/main/jni/badvpn/examples/brandom2_test.c @@ -0,0 +1,65 @@ +/** + * @file brandom2_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#define NUM_NUMBERS 10 + +static BRandom2 brandom; + +int main (int argc, char *argv[]) +{ + int ret = 1; + + if (!BRandom2_Init(&brandom, 0)) { + DEBUG("BRandom2_Init failed"); + goto fail0; + } + + uint32_t numbers[NUM_NUMBERS]; + if (!BRandom2_GenBytes(&brandom, numbers, sizeof(numbers))) { + DEBUG("BRandom2_GenBytes failed"); + goto fail1; + } + + for (int i = 0; i < NUM_NUMBERS; i++) { + printf("%"PRIu32"\n", numbers[i]); + } + + ret = 0; + +fail1: + BRandom2_Free(&brandom); +fail0: + return ret; +} diff --git a/app/src/main/jni/badvpn/examples/btimer_example.c b/app/src/main/jni/badvpn/examples/btimer_example.c new file mode 100644 index 0000000..c4d8d54 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/btimer_example.c @@ -0,0 +1,84 @@ +/** + * @file btimer_example.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +// gives average firing rate 100kHz +#define TIMER_NUM 500 +#define TIMER_MODULO 10 + +BReactor sys; + +void handle_timer (BTimer *bt) +{ + #ifdef BADVPN_USE_WINAPI + btime_t time = btime_gettime() + rand()%TIMER_MODULO; + #else + btime_t time = btime_gettime() + random()%TIMER_MODULO; + #endif + BReactor_SetTimerAbsolute(&sys, bt, time); +} + +int main () +{ + BLog_InitStdout(); + + #ifdef BADVPN_USE_WINAPI + srand(time(NULL)); + #else + srandom(time(NULL)); + #endif + + // init time + BTime_Init(); + + if (!BReactor_Init(&sys)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + BTimer timers[TIMER_NUM]; + + int i; + for (i=0; i + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define USE_COUNTS 0 +#define USE_ASSOC 1 + +typedef size_t entry_index; +#define MAX_INDICES SIZE_MAX + +typedef uint32_t entry_key; + +typedef uint8_t assoc_value; +typedef uint64_t assoc_sum; + +struct entry { + entry_index tree_child[2]; + entry_index tree_parent; + int8_t tree_balance; +#if USE_COUNTS + size_t tree_count; +#endif +#if USE_ASSOC + assoc_value assoc_value; + assoc_sum assoc_sum; +#endif + entry_key key; +}; + +typedef struct entry *entry_ptr; + +#include "cavl_test_tree.h" +#include + +#include "cavl_test_tree.h" +#include + +static void random_bytes (char *buf, size_t len) +{ + while (len > 0) { + *((unsigned char *)buf) = rand(); + buf++; + len--; + } +} + +static int uint64_less (void *user, uint64_t a, uint64_t b) +{ + return (a < b); +} + +#if USE_ASSOC +static MyTreeRef assoc_continue_last_lesser_equal (MyTree *tree, struct entry *arg, MyTreeRef ref, assoc_sum target_sum) +{ + assoc_sum cur_sum = MyTree_ExclusiveAssocPrefixSum(tree, arg, ref); + ASSERT(target_sum >= cur_sum) + while (cur_sum + ref.ptr->assoc_value <= target_sum) { + MyTreeRef next_ref = MyTree_GetNext(tree, arg, ref); + if (next_ref.link == -1) { + break; + } + cur_sum += ref.ptr->assoc_value; + ref = next_ref; + } + return ref; +} +#endif + +static void test_assoc (MyTree *tree, struct entry *arg) +{ +#if USE_ASSOC + assoc_sum sum = 0; + for (MyTreeRef ref = MyTree_GetFirst(tree, arg); ref.link != -1; ref = MyTree_GetNext(tree, arg, ref)) { + assoc_sum tree_sum = MyTree_ExclusiveAssocPrefixSum(tree, arg, ref); + ASSERT_FORCE(tree_sum == sum); + ASSERT_FORCE(MyTree_FindLastExclusiveAssocPrefixSumLesserEqual(tree, arg, sum, uint64_less, NULL).link == assoc_continue_last_lesser_equal(tree, arg, ref, sum).link); + ASSERT_FORCE(MyTree_FindLastExclusiveAssocPrefixSumLesserEqual(tree, arg, sum + 1, uint64_less, NULL).link == assoc_continue_last_lesser_equal(tree, arg, ref, sum + 1).link); + sum += ref.ptr->assoc_value; + } + ASSERT_FORCE(sum == MyTree_AssocSum(tree, arg)); +#endif +} + +int main (int argc, char *argv[]) +{ + //srand(time(NULL)); + + printf("sizeof(struct entry)=%" PRIsz "\n", sizeof(struct entry)); + + if (argc != 6) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : "")); + return 1; + } + + size_t num_keys = atoi(argv[1]); + size_t num_lookups = atoi(argv[2]); + size_t num_remove = atoi(argv[3]); + size_t do_remove = atoi(argv[4]); + size_t do_verify = atoi(argv[5]); + + printf("Allocating keys...\n"); + entry_key *keys = (entry_key *)BAllocArray(num_keys, sizeof(keys[0])); + ASSERT_FORCE(keys); + + printf("Generating random keys...\n"); + random_bytes((char *)keys, num_keys * sizeof(keys[0])); + + printf("Allocating lookup indices...\n"); + uint64_t *lookup_indices = (uint64_t *)BAllocArray(num_lookups, sizeof(lookup_indices[0])); + ASSERT_FORCE(lookup_indices); + + printf("Generating random lookup indices...\n"); + random_bytes((char *)lookup_indices, num_lookups * sizeof(lookup_indices[0])); + + printf("Allocating remove indices...\n"); + uint64_t *remove_indices = (uint64_t *)BAllocArray(num_remove, sizeof(remove_indices[0])); + ASSERT_FORCE(remove_indices); + + printf("Generating random remove indices...\n"); + random_bytes((char *)remove_indices, num_remove * sizeof(remove_indices[0])); + +#if USE_ASSOC + printf("Allocating assoc values...\n"); + assoc_value *assoc_values = (assoc_value *)BAllocArray(num_keys, sizeof(assoc_values[0])); + ASSERT_FORCE(assoc_values); + + printf("Generating random assoc values...\n"); + random_bytes((char *)assoc_values, num_keys * sizeof(assoc_values[0])); +#endif + + printf("Allocating entries...\n"); + ASSERT_FORCE(num_keys <= MAX_INDICES); + struct entry *entries = (struct entry *)BAllocArray(num_keys, sizeof(*entries)); + ASSERT_FORCE(entries); + entry_index num_used_entries = 0; + + MyTree tree; + MyTree_Init(&tree); + + struct entry *arg = entries; + + ASSERT_FORCE(MyTree_IsEmpty(&tree)); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == 0); +#endif + test_assoc(&tree, arg); + + size_t num; +#if USE_COUNTS + size_t prevNum; +#endif + + printf("Inserting random numbers...\n"); + num = 0; + for (size_t i = 0; i < num_keys; i++) { + entries[num_used_entries].key = keys[i]; +#if USE_ASSOC + entries[num_used_entries].assoc_value = assoc_values[i]; +#endif + MyTreeRef ref = {&entries[num_used_entries], num_used_entries}; + if (!MyTree_Insert(&tree, arg, ref, NULL)) { + //printf("Insert collision!\n"); + continue; + } + num_used_entries++; + num++; + } + printf("Inserted %" PRIsz ".\n", num); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == num); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + test_assoc(&tree, arg); + } + + printf("Looking up random inserted keys...\n"); + for (size_t i = 0; i < num_lookups; i++) { + entry_index idx = lookup_indices[i] % num_keys; + MyTreeRef entry = MyTree_LookupExact(&tree, arg, keys[idx]); + ASSERT_FORCE(!MyTreeIsNullRef(entry)); + } + +#if USE_COUNTS + prevNum = MyTree_Count(&tree, arg); +#endif + num = 0; + printf("Looking up and removing random inserted keys...\n"); + for (size_t i = 0; i < num_remove; i++) { + entry_index idx = remove_indices[i] % num_keys; + MyTreeRef entry = MyTree_LookupExact(&tree, arg, keys[idx]); + if (MyTreeIsNullRef(entry)) { + //printf("Remove collision!\n"); + continue; + } + ASSERT_FORCE(entry.ptr->key == keys[idx]); + MyTree_Remove(&tree, arg, entry); + num++; + } + printf("Removed %" PRIsz ".\n", num); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == prevNum - num); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + test_assoc(&tree, arg); + } + + if (do_remove) { +#if USE_COUNTS + prevNum = MyTree_Count(&tree, arg); +#endif + num = 0; + printf("Removing remaining...\n"); + + MyTreeRef cur = MyTree_GetFirst(&tree, arg); + while (!MyTreeIsNullRef(cur)) { + MyTreeRef prev = cur; + cur = MyTree_GetNext(&tree, arg, cur); + MyTree_Remove(&tree, arg, prev); + num++; + } + + printf("Removed %" PRIsz ".\n", num); + ASSERT_FORCE(MyTree_IsEmpty(&tree)); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == 0); + ASSERT_FORCE(num == prevNum); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + } + } + + printf("Freeing...\n"); + BFree(keys); + BFree(lookup_indices); + BFree(remove_indices); +#if USE_ASSOC + BFree(assoc_values); +#endif + BFree(entries); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/cavl_test_tree.h b/app/src/main/jni/badvpn/examples/cavl_test_tree.h new file mode 100644 index 0000000..463076f --- /dev/null +++ b/app/src/main/jni/badvpn/examples/cavl_test_tree.h @@ -0,0 +1,23 @@ +#define CAVL_PARAM_NAME MyTree +#define CAVL_PARAM_FEATURE_COUNTS USE_COUNTS +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_ASSOC USE_ASSOC +#define CAVL_PARAM_TYPE_ENTRY struct entry +#define CAVL_PARAM_TYPE_LINK entry_index +#define CAVL_PARAM_TYPE_KEY entry_key +#define CAVL_PARAM_TYPE_ARG entry_ptr +#define CAVL_PARAM_TYPE_COUNT size_t +#define CAVL_PARAM_TYPE_ASSOC assoc_sum +#define CAVL_PARAM_VALUE_COUNT_MAX SIZE_MAX +#define CAVL_PARAM_VALUE_NULL ((entry_index)-1) +#define CAVL_PARAM_VALUE_ASSOC_ZERO 0 +#define CAVL_PARAM_FUN_DEREF(arg, link) (&(arg)[(link)]) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1).ptr->key, (entry2).ptr->key) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2).ptr->key) +#define CAVL_PARAM_FUN_ASSOC_VALUE(arg, entry) ((entry).ptr->assoc_value) +#define CAVL_PARAM_FUN_ASSOC_OPER(arg, value1, value2) ((value1) + (value2)) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent +#define CAVL_PARAM_MEMBER_COUNT tree_count +#define CAVL_PARAM_MEMBER_ASSOC assoc_sum diff --git a/app/src/main/jni/badvpn/examples/dhcpclient_test.c b/app/src/main/jni/badvpn/examples/dhcpclient_test.c new file mode 100644 index 0000000..9601c01 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/dhcpclient_test.c @@ -0,0 +1,159 @@ +/** + * @file dhcpclient_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BRandom2 random2; +BDHCPClient dhcp; + +static void signal_handler (void *user); +static void dhcp_handler (void *unused, int event); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + goto fail0; + } + + char *ifname = argv[1]; + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BRandom2_Init(&random2, 0)) { + DEBUG("BRandom2_Init failed"); + goto fail1a; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + struct BDHCPClient_opts opts = {}; + + if (!BDHCPClient_Init(&dhcp, ifname, opts, &reactor, &random2, dhcp_handler, NULL)) { + DEBUG("BDHCPClient_Init failed"); + goto fail3; + } + + BReactor_Exec(&reactor); + + BDHCPClient_Free(&dhcp); +fail3: + BSignal_Finish(); +fail2: + BRandom2_Free(&random2); +fail1a: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 0); +} + +void dhcp_handler (void *unused, int event) +{ + switch (event) { + case BDHCPCLIENT_EVENT_UP: { + printf("DHCP: up"); + + uint32_t ip; + uint8_t *ipb = (void *)&ip; + + BDHCPClient_GetClientIP(&dhcp, &ip); + printf(" IP=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + + BDHCPClient_GetClientMask(&dhcp, &ip); + printf(" Mask=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + + if (BDHCPClient_GetRouter(&dhcp, &ip)) { + printf(" Router=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + } + + uint32_t dns[BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS]; + int num = BDHCPClient_GetDNS(&dhcp, dns, BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS); + for (int i = 0; i < num; i++) { + ip=dns[i]; + printf(" DNS=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + } + + printf("\n"); + } break; + + case BDHCPCLIENT_EVENT_DOWN: { + printf("DHCP: down\n"); + } break; + + case BDHCPCLIENT_EVENT_ERROR: { + printf("DHCP: error\n"); + + // exit reactor + BReactor_Quit(&reactor, 0); + } break; + + default: + ASSERT(0); + } +} diff --git a/app/src/main/jni/badvpn/examples/emscripten_test.c b/app/src/main/jni/badvpn/examples/emscripten_test.c new file mode 100644 index 0000000..52b0351 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/emscripten_test.c @@ -0,0 +1,71 @@ +/** + * @file emscripten_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +BReactor reactor; +BTimer timer; +BPending job; + +static void timer_handler (void *unused) +{ + printf("timer_handler %"PRIu64"\n", btime_gettime()); + + BPending_Set(&job); + BReactor_SetTimer(&reactor, &timer); +} + +static void job_handler (void *unused) +{ + printf("job_handler %"PRIu64"\n", btime_gettime()); +} + +int main () +{ + BTime_Init(); + + BReactor_EmscriptenInit(&reactor); + + BTimer_Init(&timer, 500, timer_handler, NULL); + BReactor_SetTimer(&reactor, &timer); + + BPending_Init(&job, BReactor_PendingGroup(&reactor), job_handler, NULL); + BPending_Set(&job); + + BReactor_EmscriptenSync(&reactor); + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/fairqueue_test.c b/app/src/main/jni/badvpn/examples/fairqueue_test.c new file mode 100644 index 0000000..482e086 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/fairqueue_test.c @@ -0,0 +1,145 @@ +/** + * @file fairqueue_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define OUTPUT_INTERVAL 0 +#define REMOVE_INTERVAL 1 +#define NUM_INPUTS 3 + +BReactor reactor; +TimerPacketSink sink; +PacketPassFairQueue fq; +PacketPassFairQueueFlow flows[NUM_INPUTS]; +FastPacketSource sources[NUM_INPUTS]; +char *data[] = {"0 data", "1 datadatadata", "2 datadatadatadatadata"}; +BTimer timer; +int current_cancel; + +static void init_input (int i) +{ + PacketPassFairQueueFlow_Init(&flows[i], &fq); + FastPacketSource_Init(&sources[i], PacketPassFairQueueFlow_GetInput(&flows[i]), (uint8_t *)data[i], strlen(data[i]), BReactor_PendingGroup(&reactor)); +} + +static void free_input (int i) +{ + FastPacketSource_Free(&sources[i]); + PacketPassFairQueueFlow_Free(&flows[i]); +} + +static void reset_input (void) +{ + PacketPassFairQueueFlow_AssertFree(&flows[current_cancel]); + + printf("removing %d\n", current_cancel); + + // remove flow + free_input(current_cancel); + + // init flow + init_input(current_cancel); + + // increment cancel + current_cancel = (current_cancel + 1) % NUM_INPUTS; + + // reset timer + BReactor_SetTimer(&reactor, &timer); +} + +static void flow_handler_busy (void *user) +{ + PacketPassFairQueueFlow_AssertFree(&flows[current_cancel]); + + reset_input(); +} + +static void timer_handler (void *user) +{ + // if flow is busy, request cancel and wait for it + if (PacketPassFairQueueFlow_IsBusy(&flows[current_cancel])) { + printf("cancelling %d\n", current_cancel); + PacketPassFairQueueFlow_RequestCancel(&flows[current_cancel]); + PacketPassFairQueueFlow_SetBusyHandler(&flows[current_cancel], flow_handler_busy, NULL); + return; + } + + reset_input(); +} + +int main () +{ + // initialize logging + BLog_InitStdout(); + + // init time + BTime_Init(); + + // initialize reactor + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + // initialize sink + TimerPacketSink_Init(&sink, &reactor, 500, OUTPUT_INTERVAL); + + // initialize queue + if (!PacketPassFairQueue_Init(&fq, TimerPacketSink_GetInput(&sink), BReactor_PendingGroup(&reactor), 1, 1)) { + DEBUG("PacketPassFairQueue_Init failed"); + return 1; + } + + // initialize inputs + for (int i = 0; i < NUM_INPUTS; i++) { + init_input(i); + } + + // init cancel timer + BTimer_Init(&timer, REMOVE_INTERVAL, timer_handler, NULL); + BReactor_SetTimer(&reactor, &timer); + + // init cancel counter + current_cancel = 0; + + // run reactor + int ret = BReactor_Exec(&reactor); + BReactor_Free(&reactor); + return ret; +} diff --git a/app/src/main/jni/badvpn/examples/fairqueue_test2.c b/app/src/main/jni/badvpn/examples/fairqueue_test2.c new file mode 100644 index 0000000..0fe9d34 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/fairqueue_test2.c @@ -0,0 +1,93 @@ +/** + * @file fairqueue_test2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SINK_TIMER 0 + +int main () +{ + // initialize logging + BLog_InitStdout(); + + // init time + BTime_Init(); + + // initialize reactor + BReactor reactor; + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + // initialize sink + RandomPacketSink sink; + RandomPacketSink_Init(&sink, &reactor, 500, SINK_TIMER); + + // initialize queue + PacketPassFairQueue fq; + if (!PacketPassFairQueue_Init(&fq, RandomPacketSink_GetInput(&sink), BReactor_PendingGroup(&reactor), 0, 1)) { + DEBUG("PacketPassFairQueue_Init failed"); + return 1; + } + + // initialize source 1 + PacketPassFairQueueFlow flow1; + PacketPassFairQueueFlow_Init(&flow1, &fq); + FastPacketSource source1; + char data1[] = "data1"; + FastPacketSource_Init(&source1, PacketPassFairQueueFlow_GetInput(&flow1), (uint8_t *)data1, strlen(data1), BReactor_PendingGroup(&reactor)); + + // initialize source 2 + PacketPassFairQueueFlow flow2; + PacketPassFairQueueFlow_Init(&flow2, &fq); + FastPacketSource source2; + char data2[] = "data2data2"; + FastPacketSource_Init(&source2, PacketPassFairQueueFlow_GetInput(&flow2), (uint8_t *)data2, strlen(data2), BReactor_PendingGroup(&reactor)); + + // initialize source 3 + PacketPassFairQueueFlow flow3; + PacketPassFairQueueFlow_Init(&flow3, &fq); + FastPacketSource source3; + char data3[] = "data3data3data3data3data3data3data3data3data3"; + FastPacketSource_Init(&source3, PacketPassFairQueueFlow_GetInput(&flow3), (uint8_t *)data3, strlen(data3), BReactor_PendingGroup(&reactor)); + + // run reactor + int ret = BReactor_Exec(&reactor); + BReactor_Free(&reactor); + return ret; +} diff --git a/app/src/main/jni/badvpn/examples/indexedlist_test.c b/app/src/main/jni/badvpn/examples/indexedlist_test.c new file mode 100644 index 0000000..d5282c0 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/indexedlist_test.c @@ -0,0 +1,95 @@ +/** + * @file indexedlist_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +IndexedList il; + +struct elem { + int value; + IndexedListNode node; +}; + +static void elem_insert (struct elem *e, int value, uint64_t index) +{ + e->value = value; + IndexedList_InsertAt(&il, &e->node, index); +} + +static void remove_at (uint64_t index) +{ + IndexedListNode *n = IndexedList_GetAt(&il, index); + struct elem *e = UPPER_OBJECT(n, struct elem, node); + IndexedList_Remove(&il, &e->node); +} + +static void print_list (void) +{ + for (uint64_t i = 0; i < IndexedList_Count(&il); i++) { + IndexedListNode *n = IndexedList_GetAt(&il, i); + struct elem *e = UPPER_OBJECT(n, struct elem, node); + printf("%d ", e->value); + } + printf("\n"); +} + +int main (int argc, char *argv[]) +{ + IndexedList_Init(&il); + + struct elem arr[100]; + + print_list(); + + elem_insert(&arr[0], 1, 0); + print_list(); + elem_insert(&arr[1], 2, 0); + print_list(); + elem_insert(&arr[2], 3, 0); + print_list(); + elem_insert(&arr[3], 4, 0); + print_list(); + elem_insert(&arr[4], 5, 0); + print_list(); + elem_insert(&arr[5], 6, 0); + print_list(); + + elem_insert(&arr[6], 7, 1); + print_list(); + + remove_at(0); + print_list(); + + remove_at(5); + print_list(); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/ipaddr6_test.c b/app/src/main/jni/badvpn/examples/ipaddr6_test.c new file mode 100644 index 0000000..7da486d --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ipaddr6_test.c @@ -0,0 +1,169 @@ +/** + * @file ipaddr6_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include + +#define PRINT_TEST(addr_bytes, string) \ + { \ + struct ipv6_addr addr = {addr_bytes}; \ + char str[IPADDR6_PRINT_MAX]; \ + ipaddr6_print_addr(addr, str); \ + ASSERT_FORCE(!strcmp(str, (string))); \ + struct ipv6_addr parsed_addr; \ + int res = ipaddr6_parse_ipv6_addr_bin(str, strlen(str), &parsed_addr); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(!memcmp(parsed_addr.bytes, addr.bytes, 16)); \ + } + +#define PARSE_TEST(string, addr_bytes) \ + { \ + struct ipv6_addr exp_addr = {addr_bytes}; \ + struct ipv6_addr addr; \ + int res = ipaddr6_parse_ipv6_addr_bin((string), strlen((string)), &addr); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(!memcmp(addr.bytes, exp_addr.bytes, 16)); \ + } + +#define PARSE_FAIL_TEST(string) \ + { \ + struct ipv6_addr addr; \ + int res = ipaddr6_parse_ipv6_addr_bin((string), strlen((string)), &addr); \ + ASSERT_FORCE(!res); \ + } + +#define MASK_TEST(mask_bytes, prefix) \ + { \ + struct ipv6_addr mask = {mask_bytes}; \ + int parsed_prefix; \ + int res = ipaddr6_ipv6_prefix_from_mask(mask, &parsed_prefix); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(parsed_prefix == (prefix)); \ + struct ipv6_addr generated_mask; \ + ipaddr6_ipv6_mask_from_prefix(parsed_prefix, &generated_mask); \ + ASSERT_FORCE(!memcmp(generated_mask.bytes, mask.bytes, 16)); \ + } + +#define PASS(...) __VA_ARGS__ + +int main () +{ + PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "::1") + PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), "::") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),"2001:db8::1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01}), "2001:db8::2:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "2001:db8::1:0:0:1") + + PARSE_TEST("::", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("::1", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) + PARSE_TEST("::abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd})) + PARSE_TEST("::0123:abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x23, 0xab, 0xcd})) + PARSE_TEST("abcd::", PASS({0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("abcd:0123::", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("1::2", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})) + PARSE_TEST("abcd:0123::3210:dcba", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("4567:abcd:0123::3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210::", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0x00, 0x00})) + PARSE_TEST("::4567:abcd:0123:1111:2222:3333:3210", PASS({0x00, 0x00, 0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10})) + PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("04567:000abcd:00000123:01111:2222:03333:0003210:0dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("::1.2.3.4", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ff::1.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ff::0.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04})) + PARSE_TEST("1:2:3:4:5:6:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", PASS({0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) + PARSE_TEST("1::fffa:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("1::fffa:0.0.0.0", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00})) + + PARSE_FAIL_TEST("") + PARSE_FAIL_TEST(":") + PARSE_FAIL_TEST("a") + PARSE_FAIL_TEST("a:b") + PARSE_FAIL_TEST(":b") + PARSE_FAIL_TEST("b:") + PARSE_FAIL_TEST("1:2:3:4:5:6:7") + PARSE_FAIL_TEST(":1:2:3:4:5:6:7") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:") + PARSE_FAIL_TEST(":::") + PARSE_FAIL_TEST("::a::") + PARSE_FAIL_TEST("::a::b") + PARSE_FAIL_TEST("c::a::b") + PARSE_FAIL_TEST("c::a::") + PARSE_FAIL_TEST("10000::") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9") + PARSE_FAIL_TEST("1:2:3:4::5:6:7:8:9") + PARSE_FAIL_TEST("::1:2:3:4:5:6:7:8:9") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9::") + PARSE_FAIL_TEST("a::b:") + PARSE_FAIL_TEST(":a::b") + PARSE_FAIL_TEST("::g") + PARSE_FAIL_TEST("::1.2") + PARSE_FAIL_TEST("::1.2.3.4.5") + PARSE_FAIL_TEST("::01.2.3.4") + PARSE_FAIL_TEST("::1.2.3.04") + PARSE_FAIL_TEST("::1.2.3.256") + PARSE_FAIL_TEST("1.2.3.4") + PARSE_FAIL_TEST("::8259.2.473.256") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:1.2.3.4") + PARSE_FAIL_TEST("1:2:3:4:5:1.2.3.4") + PARSE_FAIL_TEST("::1.2.3.4::") + PARSE_FAIL_TEST("::1.2.3.4:1") + PARSE_FAIL_TEST("localhost6") + + MASK_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 0) + MASK_TEST(PASS({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 1) + MASK_TEST(PASS({0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 2) + MASK_TEST(PASS({0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 3) + MASK_TEST(PASS({0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 4) + MASK_TEST(PASS({0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 5) + MASK_TEST(PASS({0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 6) + MASK_TEST(PASS({0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 7) + MASK_TEST(PASS({0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 8) + + MASK_TEST(PASS({0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 9) + MASK_TEST(PASS({0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 10) + MASK_TEST(PASS({0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 11) + MASK_TEST(PASS({0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 12) + MASK_TEST(PASS({0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 13) + MASK_TEST(PASS({0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 14) + MASK_TEST(PASS({0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 15) + MASK_TEST(PASS({0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 16) + + MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE}), 127) + MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}), 128) + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/ncd_parser_test.c b/app/src/main/jni/badvpn/examples/ncd_parser_test.c new file mode 100644 index 0000000..ac913b5 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncd_parser_test.c @@ -0,0 +1,294 @@ +/** + * @file ncd_parser_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int generate_val (NCDValue *value, ExpString *out_str) +{ + switch (NCDValue_Type(value)) { + case NCDVALUE_STRING: { + const char *str = NCDValue_StringValue(value); + size_t len = NCDValue_StringLength(value); + + if (!ExpString_AppendChar(out_str, '"')) { + goto fail; + } + + for (size_t i = 0; i < len; i++) { + if (str[i] == '\0') { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02"PRIx8, (uint8_t)str[i]); + + if (!ExpString_Append(out_str, buf)) { + goto fail; + } + + continue; + } + + if (str[i] == '"' || str[i] == '\\') { + if (!ExpString_AppendChar(out_str, '\\')) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, str[i])) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, '"')) { + goto fail; + } + } break; + + case NCDVALUE_LIST: { + if (!ExpString_AppendChar(out_str, '{')) { + goto fail; + } + + int is_first = 1; + + for (NCDValue *e = NCDValue_ListFirst(value); e; e = NCDValue_ListNext(value, e)) { + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + goto fail; + } + } + + if (!generate_val(e, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, '}')) { + goto fail; + } + } break; + + case NCDVALUE_MAP: { + if (!ExpString_AppendChar(out_str, '[')) { + goto fail; + } + + int is_first = 1; + + for (NCDValue *ekey = NCDValue_MapFirstKey(value); ekey; ekey = NCDValue_MapNextKey(value, ekey)) { + NCDValue *eval = NCDValue_MapKeyValue(value, ekey); + + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + goto fail; + } + } + + if (!generate_val(ekey, out_str)) { + goto fail; + } + + if (!ExpString_AppendChar(out_str, ':')) { + goto fail; + } + + if (!generate_val(eval, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, ']')) { + goto fail; + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static void print_indent (unsigned int indent) +{ + while (indent > 0) { + printf(" "); + indent--; + } +} + +static void print_value (NCDValue *v, unsigned int indent) +{ + ExpString estr; + if (!ExpString_Init(&estr)) { + DEBUG("ExpString_Init failed"); + exit(1); + } + + if (!generate_val(v, &estr)) { + DEBUG("generate_val failed"); + exit(1); + } + + print_indent(indent); + printf("%s\n", ExpString_Get(&estr)); + + ExpString_Free(&estr); +} + +static void print_block (NCDBlock *block, unsigned int indent) +{ + for (NCDStatement *st = NCDBlock_FirstStatement(block); st; st = NCDBlock_NextStatement(block, st)) { + const char *name = NCDStatement_Name(st) ? NCDStatement_Name(st) : ""; + + switch (NCDStatement_Type(st)) { + case NCDSTATEMENT_REG: { + const char *objname = NCDStatement_RegObjName(st) ? NCDStatement_RegObjName(st) : ""; + const char *cmdname = NCDStatement_RegCmdName(st); + + print_indent(indent); + printf("reg name=%s objname=%s cmdname=%s args:\n", name, objname, cmdname); + + print_value(NCDStatement_RegArgs(st), indent + 2); + } break; + + case NCDSTATEMENT_IF: { + print_indent(indent); + printf("if name=%s\n", name); + + NCDIfBlock *ifb = NCDStatement_IfBlock(st); + + for (NCDIf *ifc = NCDIfBlock_FirstIf(ifb); ifc; ifc = NCDIfBlock_NextIf(ifb, ifc)) { + print_indent(indent + 2); + printf("if\n"); + + print_value(NCDIf_Cond(ifc), indent + 4); + + print_indent(indent + 2); + printf("then\n"); + + print_block(NCDIf_Block(ifc), indent + 4); + } + + if (NCDStatement_IfElse(st)) { + print_indent(indent + 2); + printf("else\n"); + + print_block(NCDStatement_IfElse(st), indent + 4); + } + } break; + + case NCDSTATEMENT_FOREACH: { + const char *name1 = NCDStatement_ForeachName1(st); + const char *name2 = NCDStatement_ForeachName2(st) ? NCDStatement_ForeachName2(st) : ""; + + print_indent(indent); + printf("foreach name=%s name1=%s name2=%s\n", name, name1, name2); + + print_block(NCDStatement_ForeachBlock(st), indent + 2); + } break; + + default: ASSERT(0); + } + } +} + +int main (int argc, char **argv) +{ + int res = 1; + + if (argc != 3) { + printf("Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + int desugar = atoi(argv[1]); + char *text = argv[2]; + + BLog_InitStdout(); + + // parse + NCDProgram prog; + if (!NCDConfigParser_Parse(text, strlen(text), &prog)) { + DEBUG("NCDConfigParser_Parse failed"); + goto fail1; + } + + // desugar + if (desugar) { + if (!NCDSugar_Desugar(&prog)) { + DEBUG("NCDSugar_Desugar failed"); + goto fail2; + } + } + + // print + for (NCDProgramElem *elem = NCDProgram_FirstElem(&prog); elem; elem = NCDProgram_NextElem(&prog, elem)) { + switch (NCDProgramElem_Type(elem)) { + case NCDPROGRAMELEM_PROCESS: { + NCDProcess *p = NCDProgramElem_Process(elem); + printf("process name=%s is_template=%d\n", NCDProcess_Name(p), NCDProcess_IsTemplate(p)); + print_block(NCDProcess_Block(p), 2); + } break; + + case NCDPROGRAMELEM_INCLUDE: { + printf("include path=%s\n", NCDProgramElem_IncludePathData(elem)); + } break; + + case NCDPROGRAMELEM_INCLUDE_GUARD: { + printf("include_guard id=%s\n", NCDProgramElem_IncludeGuardIdData(elem)); + } break; + + default: ASSERT(0); + } + } + + res = 0; +fail2: + NCDProgram_Free(&prog); +fail1: + BLog_Free(); +fail0: + return res; +} diff --git a/app/src/main/jni/badvpn/examples/ncd_tokenizer_test.c b/app/src/main/jni/badvpn/examples/ncd_tokenizer_test.c new file mode 100644 index 0000000..84f90eb --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncd_tokenizer_test.c @@ -0,0 +1,149 @@ +/** + * @file ncd_tokenizer_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +int error; + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + if (token == NCD_ERROR) { + printf("line %zu, character %zu: tokenizer error\n", line, line_char); + error = 1; + return 0; + } + + switch (token) { + case NCD_EOF: + printf("eof\n"); + break; + case NCD_TOKEN_CURLY_OPEN: + printf("curly_open\n"); + break; + case NCD_TOKEN_CURLY_CLOSE: + printf("curly_close\n"); + break; + case NCD_TOKEN_ROUND_OPEN: + printf("round_open\n"); + break; + case NCD_TOKEN_ROUND_CLOSE: + printf("round_close\n"); + break; + case NCD_TOKEN_SEMICOLON: + printf("semicolon\n"); + break; + case NCD_TOKEN_DOT: + printf("dot\n"); + break; + case NCD_TOKEN_COMMA: + printf("comma\n"); + break; + case NCD_TOKEN_PROCESS: + printf("process\n"); + break; + case NCD_TOKEN_NAME: + printf("name %s\n", value); + free(value); + break; + case NCD_TOKEN_STRING: + printf("string %s\n", value); + free(value); + break; + case NCD_TOKEN_ARROW: + printf("arrow\n"); + break; + case NCD_TOKEN_TEMPLATE: + printf("template\n"); + break; + case NCD_TOKEN_COLON: + printf("colon\n"); + break; + case NCD_TOKEN_BRACKET_OPEN: + printf("bracket open\n"); + break; + case NCD_TOKEN_BRACKET_CLOSE: + printf("bracket close\n"); + break; + case NCD_TOKEN_IF: + printf("if\n"); + break; + case NCD_TOKEN_ELIF: + printf("elif\n"); + break; + case NCD_TOKEN_ELSE: + printf("else\n"); + break; + case NCD_TOKEN_FOREACH: + printf("foreach\n"); + break; + case NCD_TOKEN_AS: + printf("as\n"); + break; + case NCD_TOKEN_INCLUDE: + printf("include\n"); + break; + case NCD_TOKEN_INCLUDE_GUARD: + printf("include_guard\n"); + break; + default: + ASSERT(0); + } + + return 1; +} + +int main (int argc, char **argv) +{ + if (argc < 1) { + return 1; + } + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + BLog_InitStdout(); + + error = 0; + + NCDConfigTokenizer_Tokenize(argv[1], strlen(argv[1]), tokenizer_output, NULL); + + if (error) { + return 1; + } + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/ncd_value_parser_test.c b/app/src/main/jni/badvpn/examples/ncd_value_parser_test.c new file mode 100644 index 0000000..cf1915d --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncd_value_parser_test.c @@ -0,0 +1,78 @@ +/** + * @file ncd_value_parser_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ + int res = 1; + + if (argc != 2) { + printf("Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + BLog_InitStdout(); + + NCDValMem mem; + NCDValMem_Init(&mem); + + // parse + NCDValRef val; + if (!NCDValParser_Parse(argv[1], strlen(argv[1]), &mem, &val)) { + DEBUG("NCDValParser_Parse failed"); + goto fail1; + } + + // generate value string + char *str = NCDValGenerator_Generate(val); + if (!str) { + DEBUG("NCDValGenerator_Generate failed"); + goto fail1; + } + + // print value string + printf("%s\n", str); + + res = 0; + + free(str); +fail1: + NCDValMem_Free(&mem); + BLog_Free(); +fail0: + return res; +} diff --git a/app/src/main/jni/badvpn/examples/ncdinterfacemonitor_test.c b/app/src/main/jni/badvpn/examples/ncdinterfacemonitor_test.c new file mode 100644 index 0000000..167f1bd --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncdinterfacemonitor_test.c @@ -0,0 +1,150 @@ +/** + * @file ncdinterfacemonitor_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +NCDInterfaceMonitor monitor; + +static void signal_handler (void *user); +static void monitor_handler (void *unused, struct NCDInterfaceMonitor_event event); +static void monitor_handler_error (void *unused); + +int main (int argc, char **argv) +{ + int ret = 1; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + int ifindex; + if (!badvpn_get_iface_info(argv[1], NULL, NULL, &ifindex)) { + DEBUG("get_iface_info failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + int watch_flags = NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR; + + if (!NCDInterfaceMonitor_Init(&monitor, ifindex, watch_flags, &reactor, NULL, monitor_handler, monitor_handler_error)) { + DEBUG("NCDInterfaceMonitor_Init failed"); + goto fail3; + } + + ret = BReactor_Exec(&reactor); + + NCDInterfaceMonitor_Free(&monitor); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 1); +} + +void monitor_handler (void *unused, struct NCDInterfaceMonitor_event event) +{ + switch (event.event) { + case NCDIFMONITOR_EVENT_LINK_UP: + case NCDIFMONITOR_EVENT_LINK_DOWN: { + const char *type = (event.event == NCDIFMONITOR_EVENT_LINK_UP) ? "up" : "down"; + printf("link %s\n", type); + } break; + + case NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED: + case NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED: { + const char *type = (event.event == NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED) ? "added" : "removed"; + uint8_t *addr = (uint8_t *)&event.u.ipv4_addr.addr; + printf("ipv4 addr %s %d.%d.%d.%d/%d\n", type, (int)addr[0], (int)addr[1], (int)addr[2], (int)addr[3], event.u.ipv4_addr.addr.prefix); + } break; + + case NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED: + case NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED: { + const char *type = (event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED) ? "added" : "removed"; + + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(event.u.ipv6_addr.addr.addr, str); + + int dynamic = !!(event.u.ipv6_addr.addr_flags & NCDIFMONITOR_ADDR_FLAG_DYNAMIC); + + printf("ipv6 addr %s %s/%d scope=%"PRIu8" dynamic=%d\n", type, str, event.u.ipv6_addr.addr.prefix, event.u.ipv6_addr.scope, dynamic); + } break; + + default: ASSERT(0); + } +} + +void monitor_handler_error (void *unused) +{ + DEBUG("monitor error"); + + BReactor_Quit(&reactor, 1); +} diff --git a/app/src/main/jni/badvpn/examples/ncdudevmanager_test.c b/app/src/main/jni/badvpn/examples/ncdudevmanager_test.c new file mode 100644 index 0000000..9bbb3fe --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncdudevmanager_test.c @@ -0,0 +1,161 @@ +/** + * @file ncdudevmanager_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BUnixSignal usignal; +BProcessManager manager; +NCDUdevManager umanager; +NCDUdevClient client; + +static void signal_handler (void *user, int signo); +static void client_handler (void *unused, char *devpath, int have_map, BStringMap map); + +int main (int argc, char **argv) +{ + if (!(argc == 1 || (argc == 2 && !strcmp(argv[1], "--no-udev")))) { + fprintf(stderr, "Usage: %s [--no-udev]\n", (argc > 0 ? argv[0] : NULL)); + goto fail0; + } + + int no_udev = (argc == 2); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + if (!BUnixSignal_Init(&usignal, &reactor, set, signal_handler, NULL)) { + fprintf(stderr, "BUnixSignal_Init failed\n"); + goto fail2; + } + + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + NCDUdevManager_Init(&umanager, no_udev, &reactor, &manager); + + NCDUdevClient_Init(&client, &umanager, NULL, client_handler); + + BReactor_Exec(&reactor); + + NCDUdevClient_Free(&client); + + NCDUdevManager_Free(&umanager); + + BProcessManager_Free(&manager); +fail3: + BUnixSignal_Free(&usignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +static void signal_handler (void *user, int signo) +{ + if (signo == SIGHUP) { + fprintf(stderr, "received SIGHUP, restarting client\n"); + + NCDUdevClient_Free(&client); + NCDUdevClient_Init(&client, &umanager, NULL, client_handler); + } else { + fprintf(stderr, "received %s, exiting\n", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // exit event loop + BReactor_Quit(&reactor, 1); + } +} + +void client_handler (void *unused, char *devpath, int have_map, BStringMap map) +{ + printf("event %s\n", devpath); + + if (!have_map) { + printf(" no map\n"); + } else { + printf(" map:\n"); + + const char *name = BStringMap_First(&map); + while (name) { + printf(" %s=%s\n", name, BStringMap_Get(&map, name)); + name = BStringMap_Next(&map, name); + } + } + + const BStringMap *cache_map = NCDUdevManager_Query(&umanager, devpath); + if (!cache_map) { + printf(" no cache\n"); + } else { + printf(" cache:\n"); + + const char *name = BStringMap_First(cache_map); + while (name) { + printf(" %s=%s\n", name, BStringMap_Get(cache_map, name)); + name = BStringMap_Next(cache_map, name); + } + } + + if (have_map) { + BStringMap_Free(&map); + } + free(devpath); +} diff --git a/app/src/main/jni/badvpn/examples/ncdudevmonitor_test.c b/app/src/main/jni/badvpn/examples/ncdudevmonitor_test.c new file mode 100644 index 0000000..94b4f6f --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncdudevmonitor_test.c @@ -0,0 +1,152 @@ +/** + * @file ncdudevmonitor_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BProcessManager manager; +NCDUdevMonitor monitor; + +static void signal_handler (void *user); +static void monitor_handler_event (void *unused); +static void monitor_handler_error (void *unused, int is_error); + +int main (int argc, char **argv) +{ + int ret = 1; + + if (argc < 2 || (strcmp(argv[1], "monitor_udev") && strcmp(argv[1], "monitor_kernel") && strcmp(argv[1], "info"))) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + goto fail0; + } + + int mode; + if (!strcmp(argv[1], "monitor_udev")) { + mode = NCDUDEVMONITOR_MODE_MONITOR_UDEV; + } else if (!strcmp(argv[1], "monitor_kernel")) { + mode = NCDUDEVMONITOR_MODE_MONITOR_KERNEL; + } else { + mode = NCDUDEVMONITOR_MODE_INFO; + } + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + if (!NCDUdevMonitor_Init(&monitor, &reactor, &manager, mode, NULL, + monitor_handler_event, + monitor_handler_error + )) { + DEBUG("NCDUdevMonitor_Init failed"); + goto fail4; + } + + ret = BReactor_Exec(&reactor); + + NCDUdevMonitor_Free(&monitor); +fail4: + BProcessManager_Free(&manager); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 1); +} + +void monitor_handler_event (void *unused) +{ + // accept event + NCDUdevMonitor_Done(&monitor); + + if (NCDUdevMonitor_IsReadyEvent(&monitor)) { + printf("ready\n"); + return; + } + + printf("event\n"); + + int num_props = NCDUdevMonitor_GetNumProperties(&monitor); + for (int i = 0; i < num_props; i++) { + const char *name; + const char *value; + NCDUdevMonitor_GetProperty(&monitor, i, &name, &value); + printf(" %s=%s\n", name, value); + } +} + +void monitor_handler_error (void *unused, int is_error) +{ + if (is_error) { + DEBUG("monitor error"); + } else { + DEBUG("monitor finished"); + } + + BReactor_Quit(&reactor, (is_error ? 1 : 0)); +} diff --git a/app/src/main/jni/badvpn/examples/ncdval_test.c b/app/src/main/jni/badvpn/examples/ncdval_test.c new file mode 100644 index 0000000..6933ed0 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncdval_test.c @@ -0,0 +1,380 @@ +/** + * @file ncdval_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define FORCE(cmd) if (!(cmd)) { fprintf(stderr, "failed\n"); exit(1); } + +struct composed_string { + BRefTarget ref_target; + size_t length; + size_t chunk_size; + char **chunks; +}; + +static void composed_string_ref_target_func_release (BRefTarget *ref_target) +{ + struct composed_string *cs = UPPER_OBJECT(ref_target, struct composed_string, ref_target); + + size_t num_chunks = cs->length / cs->chunk_size; + if (cs->length % cs->chunk_size) { + num_chunks++; + } + + for (size_t i = 0; i < num_chunks; i++) { + BFree(cs->chunks[i]); + } + + BFree(cs->chunks); + BFree(cs); +} + +static void composed_string_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length) +{ + struct composed_string *cs = user; + ASSERT(offset < cs->length) + + *out_data = cs->chunks[offset / cs->chunk_size] + (offset % cs->chunk_size); + *out_length = cs->chunk_size - (offset % cs->chunk_size); +} + +static NCDValRef build_composed_string (NCDValMem *mem, const char *data, size_t length, size_t chunk_size) +{ + ASSERT(chunk_size > 0) + + struct composed_string *cs = BAlloc(sizeof(*cs)); + if (!cs) { + goto fail0; + } + + cs->length = length; + cs->chunk_size = chunk_size; + + size_t num_chunks = cs->length / cs->chunk_size; + if (cs->length % cs->chunk_size) { + num_chunks++; + } + + cs->chunks = BAllocArray(num_chunks, sizeof(cs->chunks[0])); + if (!cs->chunk_size) { + goto fail1; + } + + size_t i; + for (i = 0; i < num_chunks; i++) { + cs->chunks[i] = BAlloc(cs->chunk_size); + if (!cs->chunks[i]) { + goto fail2; + } + + size_t to_copy = length; + if (to_copy > cs->chunk_size) { + to_copy = cs->chunk_size; + } + + memcpy(cs->chunks[i], data, to_copy); + data += to_copy; + length -= to_copy; + } + + BRefTarget_Init(&cs->ref_target, composed_string_ref_target_func_release); + + NCDValComposedStringResource resource; + resource.func_getptr = composed_string_func_getptr; + resource.user = cs; + resource.ref_target = &cs->ref_target; + + NCDValRef val = NCDVal_NewComposedString(mem, resource, 0, cs->length); + BRefTarget_Deref(&cs->ref_target); + return val; + +fail2: + while (i-- > 0) { + BFree(cs->chunks[i]); + } + BFree(cs->chunks); +fail1: + BFree(cs); +fail0: + return NCDVal_NewInvalid(); +} + +static void test_string (NCDValRef str, const char *data, size_t length) +{ + FORCE( !NCDVal_IsInvalid(str) ) + FORCE( NCDVal_IsString(str) ) + FORCE( NCDVal_StringLength(str) == length ) + FORCE( NCDVal_StringHasNulls(str) == !!memchr(data, '\0', length) ) + FORCE( NCDVal_IsStringNoNulls(str) == !memchr(data, '\0', length) ) + FORCE( NCDVal_StringRegionEquals(str, 0, length, data) ) + + b_cstring cstr = NCDVal_StringCstring(str); + + for (size_t i = 0; i < length; i++) { + size_t chunk_length; + const char *chunk_data = b_cstring_get(cstr, i, length - i, &chunk_length); + + FORCE( chunk_length > 0 ) + FORCE( chunk_length <= length - i ) + FORCE( !memcmp(chunk_data, data + i, chunk_length) ) + FORCE( NCDVal_StringRegionEquals(str, i, chunk_length, data + i) ) + FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data, length), i, i, chunk_length) == 0 ) + FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data + i, length - i), i, 0, chunk_length) == 0 ) + } +} + +static void print_indent (int indent) +{ + for (int i = 0; i < indent; i++) { + printf(" "); + } +} + +static void print_value (NCDValRef val, unsigned int indent) +{ + switch (NCDVal_Type(val)) { + case NCDVAL_STRING: { + NCDValNullTermString nts; + FORCE( NCDVal_StringNullTerminate(val, &nts) ) + + print_indent(indent); + printf("string(%zu) %s\n", NCDVal_StringLength(val), nts.data); + + NCDValNullTermString_Free(&nts); + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(val); + + print_indent(indent); + printf("list(%zu)\n", NCDVal_ListCount(val)); + + for (size_t i = 0; i < count; i++) { + NCDValRef elem_val = NCDVal_ListGet(val, i); + print_value(elem_val, indent + 1); + } + } break; + + case NCDVAL_MAP: { + print_indent(indent); + printf("map(%zu)\n", NCDVal_MapCount(val)); + + for (NCDValMapElem e = NCDVal_MapOrderedFirst(val); !NCDVal_MapElemInvalid(e); e = NCDVal_MapOrderedNext(val, e)) { + NCDValRef ekey = NCDVal_MapElemKey(val, e); + NCDValRef eval = NCDVal_MapElemVal(val, e); + + print_indent(indent + 1); + printf("key=\n"); + print_value(ekey, indent + 2); + + print_indent(indent + 1); + printf("val=\n"); + print_value(eval, indent + 2); + } + } break; + } +} + +int main () +{ + int res; + + BLog_InitStdout(); + + NCDStringIndex string_index; + FORCE( NCDStringIndex_Init(&string_index) ) + + // Some basic usage of values. + + NCDValMem mem; + NCDValMem_Init(&mem); + + NCDValRef s1 = NCDVal_NewString(&mem, "Hello World"); + test_string(s1, "Hello World", 11); + ASSERT( NCDVal_IsString(s1) ) + ASSERT( !NCDVal_IsIdString(s1) ) + ASSERT( NCDVal_Type(s1) == NCDVAL_STRING ) + + NCDValRef s2 = NCDVal_NewString(&mem, "This is reeeeeeeeeeeeeallllllllyyyyy fun!"); + FORCE( !NCDVal_IsInvalid(s2) ) + + NCDValRef l1 = NCDVal_NewList(&mem, 10); + FORCE( !NCDVal_IsInvalid(l1) ) + + FORCE( NCDVal_ListAppend(l1, s1) ) + FORCE( NCDVal_ListAppend(l1, s2) ) + + print_value(s1, 0); + print_value(s2, 0); + print_value(l1, 0); + + NCDValRef k1 = NCDVal_NewString(&mem, "K1"); + FORCE( !NCDVal_IsInvalid(k1) ) + NCDValRef v1 = NCDVal_NewString(&mem, "V1"); + FORCE( !NCDVal_IsInvalid(v1) ) + + NCDValRef k2 = NCDVal_NewString(&mem, "K2"); + FORCE( !NCDVal_IsInvalid(k2) ) + NCDValRef v2 = NCDVal_NewString(&mem, "V2"); + FORCE( !NCDVal_IsInvalid(v2) ) + + NCDValRef m1 = NCDVal_NewMap(&mem, 3); + FORCE( !NCDVal_IsInvalid(m1) ) + + FORCE( NCDVal_MapInsert(m1, k1, v1, &res) && res ) + FORCE( NCDVal_MapInsert(m1, k2, v2, &res) && res ) + + ASSERT( NCDVal_MapGetValue(m1, "K1").idx == v1.idx ) + ASSERT( NCDVal_MapGetValue(m1, "K2").idx == v2.idx ) + ASSERT( NCDVal_IsInvalid(NCDVal_MapGetValue(m1, "K3")) ) + + NCDValRef ids1 = NCDVal_NewIdString(&mem, NCD_STRING_ARG1, &string_index); + test_string(ids1, "_arg1", 5); + ASSERT( !memcmp(NCDVal_StringData(ids1), "_arg1", 5) ) + ASSERT( NCDVal_StringLength(ids1) == 5 ) + ASSERT( !NCDVal_StringHasNulls(ids1) ) + ASSERT( NCDVal_StringEquals(ids1, "_arg1") ) + ASSERT( NCDVal_Type(ids1) == NCDVAL_STRING ) + ASSERT( NCDVal_IsIdString(ids1) ) + + NCDValRef ids2 = NCDVal_NewIdString(&mem, NCD_STRING_ARG2, &string_index); + test_string(ids2, "_arg2", 5); + ASSERT( !memcmp(NCDVal_StringData(ids2), "_arg2", 5) ) + ASSERT( NCDVal_StringLength(ids2) == 5 ) + ASSERT( !NCDVal_StringHasNulls(ids2) ) + ASSERT( NCDVal_StringEquals(ids2, "_arg2") ) + ASSERT( NCDVal_Type(ids2) == NCDVAL_STRING ) + ASSERT( NCDVal_IsIdString(ids2) ) + + FORCE( NCDVal_MapInsert(m1, ids1, ids2, &res) && res ) + + ASSERT( NCDVal_MapGetValue(m1, "_arg1").idx == ids2.idx ) + + print_value(m1, 0); + + NCDValRef copy = NCDVal_NewCopy(&mem, m1); + FORCE( !NCDVal_IsInvalid(copy) ) + ASSERT( NCDVal_Compare(copy, m1) == 0 ) + + NCDValMem_Free(&mem); + + // Try to make copies of a string within the same memory object. + // This is an evil test because we cannot simply copy a string using e.g. + // NCDVal_NewStringBin() - it requires that the buffer passed + // be outside the memory object of the new string. + // We use NCDVal_NewCopy(), which takes care of this by creating + // an uninitialized string using NCDVal_NewStringUninitialized() and + // then copyng the data. + + NCDValMem_Init(&mem); + + NCDValRef s[100]; + + s[0] = NCDVal_NewString(&mem, "Eeeeeeeeeeeevil."); + FORCE( !NCDVal_IsInvalid(s[0]) ) + + for (int i = 1; i < 100; i++) { + s[i] = NCDVal_NewCopy(&mem, s[i - 1]); + FORCE( !NCDVal_IsInvalid(s[i]) ) + ASSERT( NCDVal_StringEquals(s[i - 1], "Eeeeeeeeeeeevil.") ) + ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") ) + } + + for (int i = 0; i < 100; i++) { + ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") ) + } + + NCDValMem_Free(&mem); + + NCDValMem_Init(&mem); + + NCDValRef cstr1 = build_composed_string(&mem, "Hello World", 11, 3); + test_string(cstr1, "Hello World", 11); + FORCE( NCDVal_IsComposedString(cstr1) ) + FORCE( !NCDVal_IsContinuousString(cstr1) ) + FORCE( NCDVal_StringEquals(cstr1, "Hello World") ) + FORCE( !NCDVal_StringEquals(cstr1, "Hello World ") ) + FORCE( !NCDVal_StringEquals(cstr1, "Hello WorlD") ) + + NCDValRef cstr2 = build_composed_string(&mem, "GoodBye", 7, 1); + test_string(cstr2, "GoodBye", 7); + FORCE( NCDVal_IsComposedString(cstr2) ) + FORCE( !NCDVal_IsContinuousString(cstr2) ) + FORCE( NCDVal_StringEquals(cstr2, "GoodBye") ) + FORCE( !NCDVal_StringEquals(cstr2, " GoodBye") ) + FORCE( !NCDVal_StringEquals(cstr2, "goodBye") ) + + NCDValRef cstr3 = build_composed_string(&mem, "Bad\x00String", 10, 4); + test_string(cstr3, "Bad\x00String", 10); + FORCE( NCDVal_IsComposedString(cstr3) ) + FORCE( !NCDVal_IsContinuousString(cstr3) ) + + FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 1, 2, 3) < 0 ) + FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 7, 1, 4) > 0 ) + + char buf[10]; + NCDVal_StringCopyOut(cstr1, 1, 10, buf); + FORCE( !memcmp(buf, "ello World", 10) ) + + NCDValRef clist1 = NCDVal_NewList(&mem, 3); + FORCE( !NCDVal_IsInvalid(clist1) ) + FORCE( NCDVal_ListAppend(clist1, cstr1) ) + FORCE( NCDVal_ListAppend(clist1, cstr2) ) + FORCE( NCDVal_ListAppend(clist1, cstr3) ) + FORCE( NCDVal_ListCount(clist1) == 3 ) + + FORCE( NCDValMem_ConvertNonContinuousStrings(&mem, &clist1) ) + FORCE( NCDVal_ListCount(clist1) == 3 ) + + NCDValRef fixed_str1 = NCDVal_ListGet(clist1, 0); + NCDValRef fixed_str2 = NCDVal_ListGet(clist1, 1); + NCDValRef fixed_str3 = NCDVal_ListGet(clist1, 2); + + FORCE( NCDVal_IsContinuousString(fixed_str1) ) + FORCE( NCDVal_IsContinuousString(fixed_str2) ) + FORCE( NCDVal_IsContinuousString(fixed_str3) ) + + test_string(fixed_str1, "Hello World", 11); + test_string(fixed_str2, "GoodBye", 7); + test_string(fixed_str3, "Bad\x00String", 10); + + NCDValMem_Free(&mem); + + NCDStringIndex_Free(&string_index); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/ncdvalcons_test.c b/app/src/main/jni/badvpn/examples/ncdvalcons_test.c new file mode 100644 index 0000000..7a876ed --- /dev/null +++ b/app/src/main/jni/badvpn/examples/ncdvalcons_test.c @@ -0,0 +1,111 @@ +/** + * @file ncdvalcons_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +static NCDValMem mem; +static NCDValCons cons; + +static NCDValConsVal make_string (const char *data) +{ + NCDValConsVal val; + int error; + int res = NCDValCons_NewString(&cons, (const uint8_t *)data, strlen(data), &val, &error); + ASSERT_FORCE(res) + return val; +} + +static NCDValConsVal make_list (void) +{ + NCDValConsVal val; + NCDValCons_NewList(&cons, &val); + return val; +} + +static NCDValConsVal make_map (void) +{ + NCDValConsVal val; + NCDValCons_NewMap(&cons, &val); + return val; +} + +static NCDValConsVal list_prepend (NCDValConsVal list, NCDValConsVal elem) +{ + int error; + int res = NCDValCons_ListPrepend(&cons, &list, elem, &error); + ASSERT_FORCE(res) + return list; +} + +static NCDValConsVal map_insert (NCDValConsVal map, NCDValConsVal key, NCDValConsVal value) +{ + int error; + int res = NCDValCons_MapInsert(&cons, &map, key, value, &error); + ASSERT_FORCE(res) + return map; +} + +static NCDValRef complete (NCDValConsVal cval) +{ + int error; + NCDValRef val; + int res = NCDValCons_Complete(&cons, cval, &val, &error); + ASSERT_FORCE(res) + return val; +} + +int main () +{ + NCDValMem_Init(&mem); + + int res = NCDValCons_Init(&cons, &mem); + ASSERT_FORCE(res) + + NCDValRef val1 = complete(list_prepend(list_prepend(list_prepend(make_list(), make_string("hello")), make_string("world")), make_list())); + char *str1 = NCDValGenerator_Generate(val1); + ASSERT_FORCE(str1) + ASSERT_FORCE(!strcmp(str1, "{{}, \"world\", \"hello\"}")) + free(str1); + + NCDValRef val2 = complete(map_insert(map_insert(map_insert(make_map(), make_list(), make_list()), make_string("A"), make_list()), make_string("B"), make_list())); + char *str2 = NCDValGenerator_Generate(val2); + ASSERT_FORCE(str2) + printf("%s\n", str2); + ASSERT_FORCE(!strcmp(str2, "[\"A\":{}, \"B\":{}, {}:{}]")) + free(str2); + + NCDValCons_Free(&cons); + NCDValMem_Free(&mem); + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/parse_number_test.c b/app/src/main/jni/badvpn/examples/parse_number_test.c new file mode 100644 index 0000000..d393c3f --- /dev/null +++ b/app/src/main/jni/badvpn/examples/parse_number_test.c @@ -0,0 +1,130 @@ +/** + * @file parse_number_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static void test_random (int num_digits, int digit_modulo) +{ + ASSERT(num_digits > 0); + ASSERT(digit_modulo > 0); + + uint8_t digits[40]; + + for (int i = 0; i < num_digits; i++) { + digits[i] = '0' + (rand() % digit_modulo); + } + digits[num_digits] = '\0'; + + char *endptr; + uintmax_t std_num = strtoumax((const char *)digits, &endptr, 10); + int std_res = !*endptr && !(std_num == UINTMAX_MAX && errno == ERANGE); + + uintmax_t num = 0; + int res = parse_unsigned_integer_bin((const char *)digits, num_digits, &num); + + if (res != std_res) { + printf("fail1 %s\n", (const char *)digits); + ASSERT_FORCE(0); + } + + if (res && num != std_num) { + printf("fail2 %s\n", (const char *)digits); + ASSERT_FORCE(0); + } + + if (res) { + uint8_t *nozero_digits = digits; + while (*nozero_digits == '0' && nozero_digits != &digits[num_digits - 1]) { + nozero_digits++; + } + + char buf[40]; + int size = compute_decimal_repr_size(num); + generate_decimal_repr(num, buf, size); + buf[size] = '\0'; + ASSERT_FORCE(!strcmp(buf, (const char *)nozero_digits)); + } +} + +static void test_value (uintmax_t x) +{ + char str[40]; + sprintf(str, "%" PRIuMAX, x); + uintmax_t y; + int res = parse_unsigned_integer_bin(str, strlen(str), &y); + ASSERT_FORCE(res); + ASSERT_FORCE(y == x); + + char str2[40]; + int size = compute_decimal_repr_size(x); + generate_decimal_repr(x, str2, size); + str2[size] = '\0'; + + ASSERT_FORCE(!strcmp(str2, str)); +} + +static void test_value_range (uintmax_t start, uintmax_t count) +{ + uintmax_t i = start; + do { + test_value(i); + i++; + } while (i != start + count); +} + +int main () +{ + srand(time(NULL)); + + for (int num_digits = 1; num_digits <= 22; num_digits++) { + for (int i = 0; i < 1000000; i++) { + test_random(num_digits, 10); + } + for (int i = 0; i < 1000000; i++) { + test_random(num_digits, 11); + } + } + + test_value_range(UINTMAX_C(0), 5000000); + test_value_range(UINTMAX_C(100000000), 5000000); + test_value_range(UINTMAX_C(258239003), 5000000); + test_value_range(UINTMAX_C(8241096180752634), 5000000); + test_value_range(UINTMAX_C(9127982390882308083), 5000000); + test_value_range(UINTMAX_C(18446744073700000000), 20000000); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/predicate_test.c b/app/src/main/jni/badvpn/examples/predicate_test.c new file mode 100644 index 0000000..324a960 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/predicate_test.c @@ -0,0 +1,116 @@ +/** + * @file predicate_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +static int func_hello (void *user, void **args) +{ + return 1; +} + +static int func_neg (void *user, void **args) +{ + int arg = *((int *)args[0]); + + return !arg; +} + +static int func_conj (void *user, void **args) +{ + int arg1 = *((int *)args[0]); + int arg2 = *((int *)args[1]); + + return (arg1 && arg2); +} + +static int func_strcmp (void *user, void **args) +{ + char *arg1 = (char *)args[0]; + char *arg2 = (char *)args[1]; + + return (!strcmp(arg1, arg2)); +} + +static int func_error (void *user, void **args) +{ + return -1; +} + +int main (int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + // init logger + BLog_InitStdout(); + + // init predicate + BPredicate pr; + if (!BPredicate_Init(&pr, argv[1])) { + fprintf(stderr, "BPredicate_Init failed\n"); + return 1; + } + + // init functions + BPredicateFunction f_hello; + BPredicateFunction_Init(&f_hello, &pr, "hello", NULL, 0, func_hello, NULL); + int arr1[] = {PREDICATE_TYPE_BOOL}; + BPredicateFunction f_neg; + BPredicateFunction_Init(&f_neg, &pr, "neg", arr1, 1, func_neg, NULL); + int arr2[] = {PREDICATE_TYPE_BOOL, PREDICATE_TYPE_BOOL}; + BPredicateFunction f_conj; + BPredicateFunction_Init(&f_conj, &pr, "conj", arr2, 2, func_conj, NULL); + int arr3[] = {PREDICATE_TYPE_STRING, PREDICATE_TYPE_STRING}; + BPredicateFunction f_strcmp; + BPredicateFunction_Init(&f_strcmp, &pr, "strcmp", arr3, 2, func_strcmp, NULL); + BPredicateFunction f_error; + BPredicateFunction_Init(&f_error, &pr, "error", NULL, 0, func_error, NULL); + + // evaluate + int result = BPredicate_Eval(&pr); + printf("%d\n", result); + + // free functions + BPredicateFunction_Free(&f_hello); + BPredicateFunction_Free(&f_neg); + BPredicateFunction_Free(&f_conj); + BPredicateFunction_Free(&f_strcmp); + BPredicateFunction_Free(&f_error); + + // free predicate + BPredicate_Free(&pr); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/savl_test.c b/app/src/main/jni/badvpn/examples/savl_test.c new file mode 100644 index 0000000..18cf191 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/savl_test.c @@ -0,0 +1,135 @@ +/** + * @file savl_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +struct mynode; + +#include "savl_test_tree.h" +#include + +struct mynode { + int used; + int num; + MyTreeNode tree_node; +}; + +#include "savl_test_tree.h" +#include + +static void verify (MyTree *tree) +{ + printf("Verifying...\n"); + MyTree_Verify(tree, 0); +} + +int main (int argc, char **argv) +{ + int num_nodes; + int num_random_delete; + + if (argc != 3 || (num_nodes = atoi(argv[1])) <= 0 || (num_random_delete = atoi(argv[2])) < 0) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + return 1; + } + + struct mynode *nodes = (struct mynode *)BAllocArray(num_nodes, sizeof(*nodes)); + ASSERT_FORCE(nodes) + + int *values_ins = (int *)BAllocArray(num_nodes, sizeof(int)); + ASSERT_FORCE(values_ins) + + int *values = (int *)BAllocArray(num_random_delete, sizeof(int)); + ASSERT_FORCE(values) + + MyTree tree; + MyTree_Init(&tree); + verify(&tree); + + printf("Inserting random values...\n"); + int inserted = 0; + BRandom_randomize((uint8_t *)values_ins, num_nodes * sizeof(int)); + for (int i = 0; i < num_nodes; i++) { + nodes[i].num = values_ins[i]; + if (MyTree_Insert(&tree, 0, &nodes[i], NULL)) { + nodes[i].used = 1; + inserted++; + } else { + nodes[i].used = 0; + printf("Insert collision!\n"); + } + } + printf("Inserted %d entries\n", inserted); + ASSERT_FORCE(MyTree_Count(&tree, 0) == inserted) + verify(&tree); + + printf("Removing random entries...\n"); + int removed1 = 0; + BRandom_randomize((uint8_t *)values, num_random_delete * sizeof(int)); + for (int i = 0; i < num_random_delete; i++) { + int index = (((unsigned int *)values)[i] % num_nodes); + struct mynode *node = nodes + index; + if (node->used) { + MyTree_Remove(&tree, 0, node); + node->used = 0; + removed1++; + } + } + printf("Removed %d entries\n", removed1); + ASSERT_FORCE(MyTree_Count(&tree, 0) == inserted - removed1) + verify(&tree); + + printf("Removing remaining...\n"); + int removed2 = 0; + while (!MyTree_IsEmpty(&tree)) { + struct mynode *node = MyTree_GetFirst(&tree, 0); + ASSERT_FORCE(node->used) + MyTree_Remove(&tree, 0, node); + node->used = 0; + removed2++; + } + printf("Removed %d entries\n", removed2); + ASSERT_FORCE(MyTree_IsEmpty(&tree)) + ASSERT_FORCE(removed1 + removed2 == inserted) + ASSERT_FORCE(MyTree_Count(&tree, 0) == 0) + verify(&tree); + + BFree(nodes); + BFree(values_ins); + BFree(values); + + return 0; +} diff --git a/app/src/main/jni/badvpn/examples/savl_test_tree.h b/app/src/main/jni/badvpn/examples/savl_test_tree.h new file mode 100644 index 0000000..41964e9 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/savl_test_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME MyTree +#define SAVL_PARAM_FEATURE_COUNTS 1 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct mynode +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_TYPE_COUNT int +#define SAVL_PARAM_VALUE_COUNT_MAX INT_MAX +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->num, (entry2)->num) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/app/src/main/jni/badvpn/examples/stdin_input.c b/app/src/main/jni/badvpn/examples/stdin_input.c new file mode 100644 index 0000000..8ff752c --- /dev/null +++ b/app/src/main/jni/badvpn/examples/stdin_input.c @@ -0,0 +1,138 @@ +/** + * @file stdin_input.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Example program which reads stdin and waits for SIGINT and SIGTERM. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#define BUF_SIZE 64 + +BReactor reactor; +BConnection pipe_con; +BUnixSignal usignal; +StreamRecvInterface *source_if; +uint8_t buf[BUF_SIZE + 1]; + +static void signal_handler (void *user, int signo) +{ + fprintf(stderr, "received %s, exiting\n", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // exit event loop + BReactor_Quit(&reactor, 1); +} + +static void connection_handler (void *user, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + fprintf(stderr, "pipe closed\n"); + } else { + fprintf(stderr, "pipe error\n"); + } + + // exit event loop + BReactor_Quit(&reactor, (event == BCONNECTION_EVENT_RECVCLOSED ? 0 : 1)); +} + +static void input_handler_done (void *user, int data_len) +{ + // receive next chunk + StreamRecvInterface_Receiver_Recv(source_if, buf, BUF_SIZE); + + // print this chunk + buf[data_len] = '\0'; + printf("Received: '%s'\n", buf); +} + +int main () +{ + int ret = 1; + + BLog_InitStdout(); + + // init network + if (!BNetwork_GlobalInit()) { + fprintf(stderr, "BNetwork_GlobalInit failed\n"); + goto fail1; + } + + // init reactor (event loop) + if (!BReactor_Init(&reactor)) { + fprintf(stderr, "BReactor_Init failed\n"); + goto fail1; + } + + // init signal handling + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + if (!BUnixSignal_Init(&usignal, &reactor, set, signal_handler, NULL)) { + fprintf(stderr, "BUnixSignal_Init failed\n"); + goto fail2; + } + + // init BConnection object backed by the stdin fd + if (!BConnection_Init(&pipe_con, BConnection_source_pipe(0), &reactor, NULL, connection_handler)) { + fprintf(stderr, "BConnection_Init failed\n"); + goto fail3; + } + + // init connection receive interface + BConnection_RecvAsync_Init(&pipe_con); + source_if = BConnection_RecvAsync_GetIf(&pipe_con); + + // init receive done callback + StreamRecvInterface_Receiver_Init(source_if, input_handler_done, NULL); + + // receive first chunk + StreamRecvInterface_Receiver_Recv(source_if, buf, BUF_SIZE); + + // run event loop + ret = BReactor_Exec(&reactor); + + BConnection_RecvAsync_Free(&pipe_con); + BConnection_Free(&pipe_con); +fail3: + BUnixSignal_Free(&usignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); + DebugObjectGlobal_Finish(); + return ret; +} diff --git a/app/src/main/jni/badvpn/examples/substring_test.c b/app/src/main/jni/badvpn/examples/substring_test.c new file mode 100644 index 0000000..4a4b6c8 --- /dev/null +++ b/app/src/main/jni/badvpn/examples/substring_test.c @@ -0,0 +1,204 @@ +/** + * @file substring_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static int find_substring_slow (const char *str, size_t str_len, const char *sub, size_t sub_len, size_t *out_pos) +{ + ASSERT(sub_len > 0) + + if (str_len < sub_len) { + return 0; + } + + for (size_t i = 0; i <= str_len - sub_len; i++) { + if (!memcmp(str + i, sub, sub_len)) { + *out_pos = i; + return 1; + } + } + + return 0; +} + +static void print_data (const char *str, size_t str_len) +{ + while (str_len > 0) { + printf("%02"PRIx8" ", (uint8_t)(*str)); + str++; + str_len--; + } + printf("\n"); +} + +static void print_table (const size_t *table, size_t len) +{ + for (size_t i = 1; i < len; i++) { + printf("%zu ", table[i]); + } + printf("\n"); +} + +static void test_tables (int len, int count) +{ + ASSERT(len > 0) + ASSERT(count >= 0) + + char *word = (char *)BAllocSize(bsize_fromint(len)); + ASSERT_FORCE(word) + + size_t *table = (size_t *)BAllocSize(bsize_mul(bsize_fromint(len), bsize_fromsize(sizeof(table[0])))); + ASSERT_FORCE(table) + + for (int i = 0; i < count; i++) { + for (int j = 0; j < len; j++) { + word[j] = rand() % 2; + } + + build_substring_backtrack_table(word, len, table); + + for (int j = 1; j < len; j++) { + for (int k = j - 1; k >= 0; k--) { + if (!memcmp(word + j - k, word, k)) { + ASSERT_FORCE(table[j] == k) + break; + } + } + } + } + + BFree(table); + BFree(word); +} + +static void test_substring (int word_len, int text_len, int word_count, int text_count) +{ + assert(word_len > 0); + assert(text_len >= 0); + assert(word_count >= 0); + assert(text_count >= 0); + + char *word = (char *)BAllocSize(bsize_fromint(word_len)); + ASSERT_FORCE(word) + + size_t *table = (size_t *)BAllocSize(bsize_mul(bsize_fromint(word_len), bsize_fromsize(sizeof(table[0])))); + ASSERT_FORCE(table) + + char *text = (char *)BAllocSize(bsize_fromint(text_len)); + ASSERT_FORCE(text) + + for (int i = 0; i < word_count; i++) { + for (int j = 0; j < word_len; j++) { + word[j] = rand() % 2; + } + + build_substring_backtrack_table(word, word_len, table); + + for (int j = 0; j < text_count; j++) { + for (int k = 0; k < text_len; k++) { + text[k] = rand() % 2; + } + + size_t pos = 36; // to remove warning + int res = find_substring(text, text_len, word, word_len, table, &pos); + + size_t spos = 59; // to remove warning + int sres = find_substring_slow(text, text_len, word, word_len, &spos); + + ASSERT_FORCE(res == sres) + if (res) { + ASSERT_FORCE(pos == spos) + } + } + } + + BFree(text); + BFree(table); + BFree(word); +} + +int main (int argc, char *argv[]) +{ + if (argc != 7) { + printf("Usage: %s \n", (argc == 0 ? "" : argv[0])); + return 1; + } + + int tables_len = atoi(argv[1]); + int tables_count = atoi(argv[2]); + int word_len = atoi(argv[3]); + int text_len = atoi(argv[4]); + int word_count = atoi(argv[5]); + int text_count = atoi(argv[6]); + + if (tables_len <= 0 || tables_count < 0 || word_len <= 0 || text_len < 0 || word_count < 0 || text_count < 0) { + printf("Bad arguments.\n"); + return 1; + } + + srand(time(NULL)); + + test_tables(tables_len, tables_count); + + test_substring(word_len, text_len, word_count, text_count); + + { + char text[] = "aggagaa"; + char word[] = "aga"; + size_t table[sizeof(word) - 1]; + build_substring_backtrack_table(word, strlen(word), table); + + size_t pos; + int res = find_substring(text, strlen(text), word, strlen(word), table, &pos); + ASSERT_FORCE(res) + ASSERT_FORCE(pos == 3) + } + + { + char text[] = "aagagga"; + char word[] = "aga"; + size_t table[sizeof(word) - 1]; + build_substring_backtrack_table_reverse(word, strlen(word), table); + + size_t pos; + int res = find_substring_reverse(text, strlen(text), word, strlen(word), table, &pos); + ASSERT_FORCE(res) + ASSERT_FORCE(pos == 1) + } + + return 0; +} diff --git a/app/src/main/jni/badvpn/fix_flex.php b/app/src/main/jni/badvpn/fix_flex.php new file mode 100644 index 0000000..bd026d0 --- /dev/null +++ b/app/src/main/jni/badvpn/fix_flex.php @@ -0,0 +1,10 @@ +", "#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L"); +$replace = array("", "#if 1"); +$contents = str_replace($search, $replace, $contents); +$res = file_put_contents($filename, $contents); +if ($res === FALSE) exit(1); diff --git a/app/src/main/jni/badvpn/flooder/CMakeLists.txt b/app/src/main/jni/badvpn/flooder/CMakeLists.txt new file mode 100644 index 0000000..36253ab --- /dev/null +++ b/app/src/main/jni/badvpn/flooder/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(badvpn-flooder flooder.c) +target_link_libraries(badvpn-flooder system flow server_conection ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-flooder + RUNTIME DESTINATION bin +) diff --git a/app/src/main/jni/badvpn/flooder/flooder.c b/app/src/main/jni/badvpn/flooder/flooder.c new file mode 100644 index 0000000..1f3f05c --- /dev/null +++ b/app/src/main/jni/badvpn/flooder/flooder.c @@ -0,0 +1,671 @@ +/** + * @file flooder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int ssl; + char *nssdb; + char *client_cert_name; + char *server_name; + char *server_addr; + peerid_t floods[MAX_FLOODS]; + int num_floods; +} options; + +// server address we connect to +BAddr server_addr; + +// server name to use for SSL +char server_name[256]; + +// reactor +BReactor ss; + +// client certificate if using SSL +CERTCertificate *client_cert; + +// client private key if using SSL +SECKEYPrivateKey *client_key; + +// server connection +ServerConnection server; + +// whether server is ready +int server_ready; + +// my ID, defined only after server_ready +peerid_t my_id; + +// flooding output +PacketRecvInterface flood_source; +PacketProtoEncoder flood_encoder; +SinglePacketBuffer flood_buffer; + +// whether we were asked for a packet and blocked +int flood_blocking; + +// index of next peer to send packet too +int flood_next; + +/** + * Cleans up everything that can be cleaned up from inside the event loop. + */ +static void terminate (void); + +/** + * Prints command line help. + */ +static void print_help (const char *name); + +/** + * Prints program name, version and copyright notice. + */ +static void print_version (void); + +/** + * Parses command line options into the options strucute. + * + * @return 1 on success, 0 on failure + */ +static int parse_arguments (int argc, char *argv[]); + +/** + * Processes command line options. + * + * @return 1 on success, 0 on failure + */ +static int resolve_arguments (void); + +/** + * Handler invoked when program termination is requested. + */ +static void signal_handler (void *unused); + +static void server_handler_error (void *user); +static void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip); +static void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); +static void server_handler_endclient (void *user, peerid_t peer_id); +static void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +static void flood_source_handler_recv (void *user, uint8_t *data); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // resolve addresses + if (!resolve_arguments()) { + BLog(BLOG_ERROR, "Failed to resolve arguments"); + goto fail1; + } + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail1a; + } + + if (options.ssl) { + // init NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // register local NSPR file types + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail3; + } + + // init NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail2; + } + + // set cipher policy + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail3; + } + + // init server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail3; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.client_cert_name, &client_cert, &client_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail4; + } + } + + // start connecting to server + if (!ServerConnection_Init( + &server, &ss, NULL, server_addr, SC_KEEPALIVE_INTERVAL, SERVER_BUFFER_MIN_PACKETS, options.ssl, 0, client_cert, client_key, server_name, NULL, + server_handler_error, server_handler_ready, server_handler_newclient, server_handler_endclient, server_handler_message + )) { + BLog(BLOG_ERROR, "ServerConnection_Init failed"); + goto fail5; + } + + // set server not ready + server_ready = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + if (server_ready) { + ServerConnection_ReleaseBuffers(&server); + SinglePacketBuffer_Free(&flood_buffer); + PacketProtoEncoder_Free(&flood_encoder); + PacketRecvInterface_Free(&flood_source); + } + + ServerConnection_Free(&server); +fail5: + if (options.ssl) { + CERT_DestroyCertificate(client_cert); + SECKEY_DestroyPrivateKey(client_key); +fail4: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail3: + SSL_ClearSessionCache(); + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail2: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + + BSignal_Finish(); +fail1a: + BReactor_Free(&ss); +fail1: + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void terminate (void) +{ + BLog(BLOG_NOTICE, "tearing down"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--ssl --nssdb --client-cert-name ]\n" + " [--server-name ]\n" + " --server-addr \n" + " [--flood-id ] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.ssl = 0; + options.nssdb = NULL; + options.client_cert_name = NULL; + options.server_name = NULL; + options.server_addr = NULL; + options.num_floods = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.client_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--flood-id")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_floods == MAX_FLOODS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.floods[options.num_floods] = atoi(argv[i + 1]); + options.num_floods++; + i++; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.ssl != !!options.nssdb) { + fprintf(stderr, "False: --ssl <=> --nssdb\n"); + return 0; + } + + if (options.ssl != !!options.client_cert_name) { + fprintf(stderr, "False: --ssl <=> --client-cert-name\n"); + return 0; + } + + if (!options.server_addr) { + fprintf(stderr, "False: --server-addr\n"); + return 0; + } + + return 1; +} + +int resolve_arguments (void) +{ + // resolve server address + ASSERT(options.server_addr) + if (!BAddr_Parse(&server_addr, options.server_addr, server_name, sizeof(server_name))) { + BLog(BLOG_ERROR, "server addr: BAddr_Parse failed"); + return 0; + } + if (!addr_supported(server_addr)) { + BLog(BLOG_ERROR, "server addr: not supported"); + return 0; + } + + // override server name if requested + if (options.server_name) { + if (strlen(options.server_name) >= sizeof(server_name)) { + BLog(BLOG_ERROR, "server name: too long"); + return 0; + } + strcpy(server_name, options.server_name); + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +void server_handler_error (void *user) +{ + BLog(BLOG_ERROR, "server connection failed, exiting"); + + terminate(); +} + +void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip) +{ + ASSERT(!server_ready) + + // remember our ID + my_id = param_my_id; + + // init flooding + + // init source + PacketRecvInterface_Init(&flood_source, SC_MAX_ENC, flood_source_handler_recv, NULL, BReactor_PendingGroup(&ss)); + + // init encoder + PacketProtoEncoder_Init(&flood_encoder, &flood_source, BReactor_PendingGroup(&ss)); + + // init buffer + if (!SinglePacketBuffer_Init(&flood_buffer, PacketProtoEncoder_GetOutput(&flood_encoder), ServerConnection_GetSendInterface(&server), BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed, exiting"); + goto fail1; + } + + // set not blocking + flood_blocking = 0; + + // set server ready + server_ready = 1; + + BLog(BLOG_INFO, "server: ready, my ID is %d", (int)my_id); + + return; + +fail1: + PacketProtoEncoder_Free(&flood_encoder); + PacketRecvInterface_Free(&flood_source); + terminate(); +} + +void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + + BLog(BLOG_INFO, "newclient %d", (int)peer_id); +} + +void server_handler_endclient (void *user, peerid_t peer_id) +{ + ASSERT(server_ready) + + BLog(BLOG_INFO, "endclient %d", (int)peer_id); +} + +void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len) +{ + ASSERT(server_ready) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + BLog(BLOG_INFO, "message from %d", (int)peer_id); +} + +void flood_source_handler_recv (void *user, uint8_t *data) +{ + ASSERT(server_ready) + ASSERT(!flood_blocking) + if (options.num_floods > 0) { + ASSERT(flood_next >= 0) + ASSERT(flood_next < options.num_floods) + } + + if (options.num_floods == 0) { + flood_blocking = 1; + return; + } + + peerid_t peer_id = options.floods[flood_next]; + flood_next = (flood_next + 1) % options.num_floods; + + BLog(BLOG_INFO, "message to %d", (int)peer_id); + + struct sc_header header; + header.type = SCID_OUTMSG; + memcpy(data, &header, sizeof(header)); + + struct sc_client_outmsg omsg; + omsg.clientid = htol16(peer_id); + memcpy(data + sizeof(header), &omsg, sizeof(omsg)); + + memset(data + sizeof(struct sc_header) + sizeof(struct sc_client_outmsg), 0, SC_MAX_MSGLEN); + + PacketRecvInterface_Done(&flood_source, sizeof(struct sc_header) + sizeof(struct sc_client_outmsg) + SC_MAX_MSGLEN); +} diff --git a/app/src/main/jni/badvpn/flooder/flooder.h b/app/src/main/jni/badvpn/flooder/flooder.h new file mode 100644 index 0000000..c8b8443 --- /dev/null +++ b/app/src/main/jni/badvpn/flooder/flooder.h @@ -0,0 +1,37 @@ +/** + * @file flooder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "flooder" + +// server output buffer size +#define SERVER_BUFFER_MIN_PACKETS 200 + +// maximum number of peers to flood +#define MAX_FLOODS 64 diff --git a/app/src/main/jni/badvpn/flow/BufferWriter.c b/app/src/main/jni/badvpn/flow/BufferWriter.c new file mode 100644 index 0000000..b0e4129 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/BufferWriter.c @@ -0,0 +1,112 @@ +/** + * @file BufferWriter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void output_handler_recv (BufferWriter *o, uint8_t *data) +{ + ASSERT(!o->out_have) + + // set output packet + o->out_have = 1; + o->out = data; +} + +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init output + PacketRecvInterface_Init(&o->recv_interface, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no output packet + o->out_have = 0; + + DebugObject_Init(&o->d_obj); + #ifndef NDEBUG + o->d_mtu = mtu; + o->d_writing = 0; + #endif +} + +void BufferWriter_Free (BufferWriter *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->recv_interface); +} + +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->recv_interface; +} + +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) +{ + ASSERT(!o->d_writing) + DebugObject_Access(&o->d_obj); + + if (!o->out_have) { + return 0; + } + + if (buf) { + *buf = o->out; + } + + #ifndef NDEBUG + o->d_writing = 1; + #endif + + return 1; +} + +void BufferWriter_EndPacket (BufferWriter *o, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->d_mtu) + ASSERT(o->out_have) + ASSERT(o->d_writing) + DebugObject_Access(&o->d_obj); + + // set no output packet + o->out_have = 0; + + // finish packet + PacketRecvInterface_Done(&o->recv_interface, len); + + #ifndef NDEBUG + o->d_writing = 0; + #endif +} diff --git a/app/src/main/jni/badvpn/flow/BufferWriter.h b/app/src/main/jni/badvpn/flow/BufferWriter.h new file mode 100644 index 0000000..6b6a9c4 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/BufferWriter.h @@ -0,0 +1,107 @@ +/** + * @file BufferWriter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ + +#ifndef BADVPN_FLOW_BUFFERWRITER_H +#define BADVPN_FLOW_BUFFERWRITER_H + +#include + +#include +#include +#include + +/** + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ +typedef struct { + PacketRecvInterface recv_interface; + int out_have; + uint8_t *out; + DebugObject d_obj; + #ifndef NDEBUG + int d_mtu; + int d_writing; + #endif +} BufferWriter; + +/** + * Initializes the object. + * The object is initialized in not writing state. + * + * @param o the object + * @param mtu maximum input packet length + * @param pg pending group + */ +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void BufferWriter_Free (BufferWriter *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o); + +/** + * Attempts to provide a memory location for writing a packet. + * The object must be in not writing state. + * On success, the object enters writing state. + * + * @param o the object + * @param buf if not NULL, on success, the memory location will be stored here. + * It will have space for MTU bytes. + * @return 1 on success, 0 on failure + */ +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) WARN_UNUSED; + +/** + * Submits a packet written to the buffer. + * The object must be in writing state. + * Yhe object enters not writing state. + * + * @param o the object + * @param len length of the packet that was written. Must be >=0 and + * <=MTU. + */ +void BufferWriter_EndPacket (BufferWriter *o, int len); + +#endif diff --git a/app/src/main/jni/badvpn/flow/CMakeLists.txt b/app/src/main/jni/badvpn/flow/CMakeLists.txt new file mode 100644 index 0000000..6cd82f6 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/CMakeLists.txt @@ -0,0 +1,31 @@ +set(FLOW_SOURCES + PacketPassFairQueue.c + PacketPassPriorityQueue.c + PacketPassConnector.c + PacketRecvConnector.c + StreamRecvConnector.c + PacketRecvBlocker.c + PacketPassNotifier.c + PacketBuffer.c + SinglePacketBuffer.c + PacketCopier.c + PacketStreamSender.c + PacketProtoEncoder.c + PacketProtoDecoder.c + PacketProtoFlow.c + SinglePacketSender.c + BufferWriter.c + PacketPassInterface.c + PacketRecvInterface.c + StreamPassInterface.c + StreamRecvInterface.c + RouteBuffer.c + PacketRouter.c + LineBuffer.c + SingleStreamSender.c + SingleStreamReceiver.c + StreamPacketSender.c + StreamPassConnector.c + PacketPassFifoQueue.c +) +badvpn_add_library(flow "base" "" "${FLOW_SOURCES}") diff --git a/app/src/main/jni/badvpn/flow/LineBuffer.c b/app/src/main/jni/badvpn/flow/LineBuffer.c new file mode 100644 index 0000000..15c9969 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/LineBuffer.c @@ -0,0 +1,140 @@ +/** + * @file LineBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#include + +static void input_handler_done (LineBuffer *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - o->buf_used) + + // update buffer + o->buf_used += data_len; + + // look for newline + int i; + for (i = o->buf_used - data_len; i < o->buf_used; i++) { + if (o->buf[i] == o->nl_char) { + break; + } + } + + if (i < o->buf_used || o->buf_used == o->buf_size) { + if (i == o->buf_used) { + BLog(BLOG_WARNING, "line too long"); + } + + // pass to output + o->buf_consumed = (i < o->buf_used ? i + 1 : i); + PacketPassInterface_Sender_Send(o->output, o->buf, o->buf_consumed); + } else { + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + } +} + +static void output_handler_done (LineBuffer *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buf_consumed > 0) + ASSERT(o->buf_consumed <= o->buf_used) + + // update buffer + memmove(o->buf, o->buf + o->buf_consumed, o->buf_used - o->buf_consumed); + o->buf_used -= o->buf_consumed; + + // look for newline + int i; + for (i = 0; i < o->buf_used; i++) { + if (o->buf[i] == o->nl_char) { + break; + } + } + + if (i < o->buf_used || o->buf_used == o->buf_size) { + // pass to output + o->buf_consumed = (i < o->buf_used ? i + 1 : i); + PacketPassInterface_Sender_Send(o->output, o->buf, o->buf_consumed); + } else { + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + } +} + +int LineBuffer_Init (LineBuffer *o, StreamRecvInterface *input, PacketPassInterface *output, int buf_size, uint8_t nl_char) +{ + ASSERT(buf_size > 0) + ASSERT(PacketPassInterface_GetMTU(output) >= buf_size) + + // init arguments + o->input = input; + o->output = output; + o->buf_size = buf_size; + o->nl_char = nl_char; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // set buffer empty + o->buf_used = 0; + + // allocate buffer + if (!(o->buf = (uint8_t *)malloc(o->buf_size))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void LineBuffer_Free (LineBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + free(o->buf); +} diff --git a/app/src/main/jni/badvpn/flow/LineBuffer.h b/app/src/main/jni/badvpn/flow/LineBuffer.h new file mode 100644 index 0000000..6e15e32 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/LineBuffer.h @@ -0,0 +1,54 @@ +/** + * @file LineBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_FLOW_LINEBUFFER_H +#define BADVPN_FLOW_LINEBUFFER_H + +#include + +#include +#include +#include +#include + +typedef struct { + StreamRecvInterface *input; + PacketPassInterface *output; + int buf_size; + uint8_t nl_char; + int buf_used; + uint8_t *buf; + int buf_consumed; + DebugObject d_obj; +} LineBuffer; + +int LineBuffer_Init (LineBuffer *o, StreamRecvInterface *input, PacketPassInterface *output, int buf_size, uint8_t nl_char) WARN_UNUSED; +void LineBuffer_Free (LineBuffer *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketBuffer.c b/app/src/main/jni/badvpn/flow/PacketBuffer.c new file mode 100644 index 0000000..30afef6 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketBuffer.c @@ -0,0 +1,131 @@ +/** + * @file PacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (PacketBuffer *buf, int in_len); +static void output_handler_done (PacketBuffer *buf); + +void input_handler_done (PacketBuffer *buf, int in_len) +{ + ASSERT(in_len >= 0) + ASSERT(in_len <= buf->input_mtu) + DebugObject_Access(&buf->d_obj); + + // remember if buffer is empty + int was_empty = (buf->buf.output_avail < 0); + + // submit packet to buffer + ChunkBuffer2_SubmitPacket(&buf->buf, in_len); + + // if there is space, schedule receive + if (buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if buffer was empty, schedule send + if (was_empty) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +void output_handler_done (PacketBuffer *buf) +{ + DebugObject_Access(&buf->d_obj); + + // remember if buffer is full + int was_full = (buf->buf.input_avail < buf->input_mtu); + + // remove packet from buffer + ChunkBuffer2_ConsumePacket(&buf->buf); + + // if buffer was full and there is space, schedule receive + if (was_full && buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if there is more data, schedule send + if (buf->buf.output_avail >= 0) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + ASSERT(num_packets > 0) + + // init arguments + buf->input = input; + buf->output = output; + + // init input + PacketRecvInterface_Receiver_Init(buf->input, (PacketRecvInterface_handler_done)input_handler_done, buf); + + // set input MTU + buf->input_mtu = PacketRecvInterface_GetMTU(buf->input); + + // init output + PacketPassInterface_Sender_Init(buf->output, (PacketPassInterface_handler_done)output_handler_done, buf); + + // allocate buffer + int num_blocks = ChunkBuffer2_calc_blocks(buf->input_mtu, num_packets); + if (num_blocks < 0) { + goto fail0; + } + if (!(buf->buf_data = (struct ChunkBuffer2_block *)BAllocArray(num_blocks, sizeof(buf->buf_data[0])))) { + goto fail0; + } + + // init buffer + ChunkBuffer2_Init(&buf->buf, buf->buf_data, num_blocks, buf->input_mtu); + + // schedule receive + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + + DebugObject_Init(&buf->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketBuffer_Free (PacketBuffer *buf) +{ + DebugObject_Free(&buf->d_obj); + + // free buffer + BFree(buf->buf_data); +} diff --git a/app/src/main/jni/badvpn/flow/PacketBuffer.h b/app/src/main/jni/badvpn/flow/PacketBuffer.h new file mode 100644 index 0000000..6daab69 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketBuffer.h @@ -0,0 +1,77 @@ +/** + * @file PacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETBUFFER_H +#define BADVPN_FLOW_PACKETBUFFER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + int input_mtu; + PacketPassInterface *output; + struct ChunkBuffer2_block *buf_data; + ChunkBuffer2 buf; +} PacketBuffer; + +/** + * Initializes the buffer. + * Output MTU must be >= input MTU. + * + * @param buf the object + * @param input input interface + * @param output output interface + * @param num_packets minimum number of packets the buffer must hold. Must be >0. + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the buffer. + * + * @param buf the object + */ +void PacketBuffer_Free (PacketBuffer *buf); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketCopier.c b/app/src/main/jni/badvpn/flow/PacketCopier.c new file mode 100644 index 0000000..f4c7126 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketCopier.c @@ -0,0 +1,136 @@ +/** + * @file PacketCopier.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (PacketCopier *o, uint8_t *data, int data_len) +{ + ASSERT(o->in_len == -1) + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + if (!o->out_have) { + o->in_len = data_len; + o->in = data; + return; + } + + memcpy(o->out, data, data_len); + + // finish input packet + PacketPassInterface_Done(&o->input); + + // finish output packet + PacketRecvInterface_Done(&o->output, data_len); + + o->out_have = 0; +} + +static void input_handler_requestcancel (PacketCopier *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // finish input packet + PacketPassInterface_Done(&o->input); + + o->in_len = -1; +} + +static void output_handler_recv (PacketCopier *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + if (o->in_len < 0) { + o->out_have = 1; + o->out = data; + return; + } + + memcpy(data, o->in, o->in_len); + + // finish input packet + PacketPassInterface_Done(&o->input); + + // finish output packet + PacketRecvInterface_Done(&o->output, o->in_len); + + o->in_len = -1; +} + +void PacketCopier_Init (PacketCopier *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init input + PacketPassInterface_Init(&o->input, mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + + // init output + PacketRecvInterface_Init(&o->output, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no input packet + o->in_len = -1; + + // set no output packet + o->out_have = 0; + + DebugObject_Init(&o->d_obj); +} + +void PacketCopier_Free (PacketCopier *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketCopier_GetInput (PacketCopier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +PacketRecvInterface * PacketCopier_GetOutput (PacketCopier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/flow/PacketCopier.h b/app/src/main/jni/badvpn/flow/PacketCopier.h new file mode 100644 index 0000000..9b8ba86 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketCopier.h @@ -0,0 +1,90 @@ +/** + * @file PacketCopier.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which copies packets. + */ + +#ifndef BADVPN_FLOW_PACKETCOPIER_H +#define BADVPN_FLOW_PACKETCOPIER_H + +#include + +#include +#include + +/** + * Object which copies packets. + * Input is via {@link PacketPassInterface}. + * Output is via {@link PacketRecvInterface}. + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface input; + PacketRecvInterface output; + int in_len; + uint8_t *in; + int out_have; + uint8_t *out; +} PacketCopier; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. + * @param pg pending group + */ +void PacketCopier_Init (PacketCopier *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketCopier_Free (PacketCopier *o); + +/** + * Returns the input interface. + * The MTU of the interface will as in {@link PacketCopier_Init}. + * The interface will support cancel functionality. + * + * @return input interface + */ +PacketPassInterface * PacketCopier_GetInput (PacketCopier *o); + +/** + * Returns the output interface. + * The MTU of the interface will be as in {@link PacketCopier_Init}. + * + * @return output interface + */ +PacketRecvInterface * PacketCopier_GetOutput (PacketCopier *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassConnector.c b/app/src/main/jni/badvpn/flow/PacketPassConnector.c new file mode 100644 index 0000000..1a10326 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassConnector.c @@ -0,0 +1,125 @@ +/** + * @file PacketPassConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (PacketPassConnector *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + o->in = data; + + if (o->output) { + // schedule send + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +static void output_handler_done (PacketPassConnector *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // have no input packet + o->in_len = -1; + + // allow input to send more packets + PacketPassInterface_Done(&o->input); +} + +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + o->input_mtu = mtu; + + // init input + PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // have no input packet + o->in_len = -1; + + // have no output + o->output = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketPassConnector_Free (PacketPassConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output) +{ + ASSERT(!o->output) + ASSERT(PacketPassInterface_GetMTU(output) >= o->input_mtu) + DebugObject_Access(&o->d_obj); + + // set output + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // if we have an input packet, schedule send + if (o->in_len >= 0) { + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o) +{ + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // set no output + o->output = NULL; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassConnector.h b/app/src/main/jni/badvpn/flow/PacketPassConnector.h new file mode 100644 index 0000000..1a2cc6c --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassConnector.h @@ -0,0 +1,102 @@ +/** + * @file PacketPassConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_PACKETPASSCONNECTOR_H +#define BADVPN_FLOW_PACKETPASSCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ +typedef struct { + PacketPassInterface input; + int input_mtu; + int in_len; + uint8_t *in; + PacketPassInterface *output; + DebugObject d_obj; +} PacketPassConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param mtu maximum input packet size. Must be >=0. + * @param pg pending group + */ +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassConnector_Free (PacketPassConnector *o); + +/** + * Returns the input interface. + * The MTU of the interface will be as in {@link PacketPassConnector_Init}. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o); + +/** + * Connects output. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output output to connect. Its MTU must be >= MTU specified in + * {@link PacketPassConnector_Init}. + */ +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output); + +/** + * Disconnects output. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassFairQueue.c b/app/src/main/jni/badvpn/flow/PacketPassFairQueue.c new file mode 100644 index 0000000..bbfafe0 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassFairQueue.c @@ -0,0 +1,405 @@ +/** + * @file PacketPassFairQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include + +static int compare_flows (PacketPassFairQueueFlow *f1, PacketPassFairQueueFlow *f2) +{ + int cmp = B_COMPARE(f1->time, f2->time); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)f1, (uintptr_t)f2); +} + +#include "PacketPassFairQueue_tree.h" +#include + +static uint64_t get_current_time (PacketPassFairQueue *m) +{ + if (m->sending_flow) { + return m->sending_flow->time; + } + + uint64_t time = 0; // to remove warning + int have = 0; + + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (first_flow) { + ASSERT(first_flow->is_queued) + + time = first_flow->time; + have = 1; + } + + if (m->previous_flow) { + if (!have || m->previous_flow->time < time) { + time = m->previous_flow->time; + have = 1; + } + } + + return (have ? time : 0); +} + +static void increment_sent_flow (PacketPassFairQueueFlow *flow, uint64_t amount) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(amount <= FAIRQUEUE_MAX_TIME) + ASSERT(!flow->is_queued) + ASSERT(!m->sending_flow) + + // does time overflow? + if (amount > FAIRQUEUE_MAX_TIME - flow->time) { + // get time to subtract + uint64_t subtract; + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (!first_flow) { + subtract = flow->time; + } else { + ASSERT(first_flow->is_queued) + subtract = first_flow->time; + } + + // subtract time from all flows + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&m->flows_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + PacketPassFairQueueFlow *someflow = UPPER_OBJECT(list_node, PacketPassFairQueueFlow, list_node); + + // don't subtract more time than there is, except for the just finished flow, + // where we allow time to underflow and then overflow to the correct value after adding to it + if (subtract > someflow->time && someflow != flow) { + ASSERT(!someflow->is_queued) + someflow->time = 0; + } else { + someflow->time -= subtract; + } + } + } + + // add time to flow + flow->time += amount; +} + +static void schedule (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!m->freeing) + ASSERT(!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + + // get first queued flow + PacketPassFairQueueFlow *qflow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + ASSERT(qflow->is_queued) + + // remove flow from queue + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, qflow); + qflow->is_queued = 0; + + // schedule send + PacketPassInterface_Sender_Send(m->output, qflow->queued.data, qflow->queued.data_len); + m->sending_flow = qflow; + m->sending_len = qflow->queued.data_len; +} + +static void schedule_job_handler (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // remove previous flow + m->previous_flow = NULL; + + if (!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) { + schedule(m); + } +} + +static void input_handler_send (PacketPassFairQueueFlow *flow, uint8_t *data, int data_len) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow != m->sending_flow) + ASSERT(!flow->is_queued) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + if (flow == m->previous_flow) { + // remove from previous flow + m->previous_flow = NULL; + } else { + // raise time + flow->time = bmax_uint64(flow->time, get_current_time(m)); + } + + // queue flow + flow->queued.data = data; + flow->queued.data_len = data_len; + int res = PacketPassFairQueue__Tree_Insert(&m->queued_tree, 0, flow, NULL); + ASSERT_EXECUTE(res) + flow->is_queued = 1; + + if (!m->sending_flow && !BPending_IsSet(&m->schedule_job)) { + schedule(m); + } +} + +static void output_handler_done (PacketPassFairQueue *m) +{ + ASSERT(m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!BPending_IsSet(&m->schedule_job)) + ASSERT(!m->freeing) + ASSERT(!m->sending_flow->is_queued) + + PacketPassFairQueueFlow *flow = m->sending_flow; + + // sending finished + m->sending_flow = NULL; + + // remember this flow so the schedule job can remove its time if it didn's send + m->previous_flow = flow; + + // update flow time by packet size + increment_sent_flow(flow, (uint64_t)m->packet_weight + m->sending_len); + + // schedule schedule + BPending_Set(&m->schedule_job); + + // finish flow packet + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassFairQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) +{ + ASSERT(packet_weight > 0) + ASSERT(use_cancel == 0 || use_cancel == 1) + ASSERT(!use_cancel || PacketPassInterface_HasCancel(output)) + + // init arguments + m->output = output; + m->pg = pg; + m->use_cancel = use_cancel; + m->packet_weight = packet_weight; + + // make sure that (output MTU + packet_weight <= FAIRQUEUE_MAX_TIME) + if (!( + (PacketPassInterface_GetMTU(output) <= FAIRQUEUE_MAX_TIME) && + (packet_weight <= FAIRQUEUE_MAX_TIME - PacketPassInterface_GetMTU(output)) + )) { + goto fail0; + } + + // init output + PacketPassInterface_Sender_Init(m->output, (PacketPassInterface_handler_done)output_handler_done, m); + + // not sending + m->sending_flow = NULL; + + // no previous flow + m->previous_flow = NULL; + + // init queued tree + PacketPassFairQueue__Tree_Init(&m->queued_tree); + + // init flows list + LinkedList1_Init(&m->flows_list); + + // not freeing + m->freeing = 0; + + // init schedule job + BPending_Init(&m->schedule_job, m->pg, (BPending_handler)schedule_job_handler, m); + + DebugObject_Init(&m->d_obj); + DebugCounter_Init(&m->d_ctr); + return 1; + +fail0: + return 0; +} + +void PacketPassFairQueue_Free (PacketPassFairQueue *m) +{ + ASSERT(LinkedList1_IsEmpty(&m->flows_list)) + ASSERT(PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + ASSERT(!m->previous_flow) + ASSERT(!m->sending_flow) + DebugCounter_Free(&m->d_ctr); + DebugObject_Free(&m->d_obj); + + // free schedule job + BPending_Free(&m->schedule_job); +} + +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + // set freeing + m->freeing = 1; +} + +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + return PacketPassInterface_GetMTU(m->output); +} + +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m) +{ + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // init arguments + flow->m = m; + + // have no canfree handler + flow->handler_busy = NULL; + + // init input + PacketPassInterface_Init(&flow->input, PacketPassInterface_GetMTU(flow->m->output), (PacketPassInterface_handler_send)input_handler_send, flow, m->pg); + + // set time + flow->time = 0; + + // add to flows list + LinkedList1_Append(&m->flows_list, &flow->list_node); + + // is not queued + flow->is_queued = 0; + + DebugObject_Init(&flow->d_obj); + DebugCounter_Increment(&m->d_ctr); +} + +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(m->freeing || flow != m->sending_flow) + DebugCounter_Decrement(&m->d_ctr); + DebugObject_Free(&flow->d_obj); + + // remove from current flow + if (flow == m->sending_flow) { + m->sending_flow = NULL; + } + + // remove from previous flow + if (flow == m->previous_flow) { + m->previous_flow = NULL; + } + + // remove from queue + if (flow->is_queued) { + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, flow); + } + + // remove from flows list + LinkedList1_Remove(&m->flows_list, &flow->list_node); + + // free input + PacketPassInterface_Free(&flow->input); +} + +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(m->freeing || flow != m->sending_flow) + DebugObject_Access(&flow->d_obj); +} + +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + return (flow == m->sending_flow); +} + +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow == m->sending_flow) + ASSERT(m->use_cancel) + ASSERT(!m->freeing) + ASSERT(!BPending_IsSet(&m->schedule_job)) + DebugObject_Access(&flow->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(m->output); +} + +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(flow == m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // set handler + flow->handler_busy = handler; + flow->user = user; +} + +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow) +{ + DebugObject_Access(&flow->d_obj); + + return &flow->input; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassFairQueue.h b/app/src/main/jni/badvpn/flow/PacketPassFairQueue.h new file mode 100644 index 0000000..f846596 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassFairQueue.h @@ -0,0 +1,204 @@ +/** + * @file PacketPassFairQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Fair queue using {@link PacketPassInterface}. + */ + +#ifndef BADVPN_FLOW_PACKETPASSFAIRQUEUE_H +#define BADVPN_FLOW_PACKETPASSFAIRQUEUE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +// reduce this to test time overflow handling +#define FAIRQUEUE_MAX_TIME UINT64_MAX + +typedef void (*PacketPassFairQueue_handler_busy) (void *user); + +struct PacketPassFairQueueFlow_s; + +#include "PacketPassFairQueue_tree.h" +#include + +typedef struct PacketPassFairQueueFlow_s { + struct PacketPassFairQueue_s *m; + PacketPassFairQueue_handler_busy handler_busy; + void *user; + PacketPassInterface input; + uint64_t time; + LinkedList1Node list_node; + int is_queued; + struct { + PacketPassFairQueue__TreeNode tree_node; + uint8_t *data; + int data_len; + } queued; + DebugObject d_obj; +} PacketPassFairQueueFlow; + +/** + * Fair queue using {@link PacketPassInterface}. + */ +typedef struct PacketPassFairQueue_s { + PacketPassInterface *output; + BPendingGroup *pg; + int use_cancel; + int packet_weight; + struct PacketPassFairQueueFlow_s *sending_flow; + int sending_len; + struct PacketPassFairQueueFlow_s *previous_flow; + PacketPassFairQueue__Tree queued_tree; + LinkedList1 flows_list; + int freeing; + BPending schedule_job; + DebugObject d_obj; + DebugCounter d_ctr; +} PacketPassFairQueue; + +/** + * Initializes the queue. + * + * @param m the object + * @param output output interface + * @param pg pending group + * @param use_cancel whether cancel functionality is required. Must be 0 or 1. + * If 1, output must support cancel functionality. + * @param packet_weight additional weight a packet bears. Must be >0, to keep + * the queue fair for zero size packets. + * @return 1 on success, 0 on failure (because output MTU is too large) + */ +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) WARN_UNUSED; + +/** + * Frees the queue. + * All flows must have been freed. + * + * @param m the object + */ +void PacketPassFairQueue_Free (PacketPassFairQueue *m); + +/** + * Prepares for freeing the entire queue. Must be called to allow freeing + * the flows in the process of freeing the entire queue. + * After this function is called, flows and the queue must be freed + * before any further I/O. + * May be called multiple times. + * The queue enters freeing state. + * + * @param m the object + */ +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m); + +/** + * Returns the MTU of the queue. + * + * @param m the object + */ +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m); + +/** + * Initializes a queue flow. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param m queue to attach to + */ +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m); + +/** + * Frees a queue flow. + * Unless the queue is in freeing state: + * - The flow must not be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * - Must not be called from queue calls to output. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow); + +/** + * Does nothing. + * It must be possible to free the flow (see {@link PacketPassFairQueueFlow_Free}). + * + * @param flow the object + */ +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow); + +/** + * Determines if the flow is busy. If the flow is considered busy, it must not + * be freed. At any given time, at most one flow will be indicated as busy. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @return 0 if not busy, 1 is busy + */ +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow); + +/** + * Requests the output to stop processing the current packet as soon as possible. + * Cancel functionality must be enabled for the queue. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow); + +/** + * Sets up a callback to be called when the flow is no longer busy. + * The handler will be called as soon as the flow is no longer busy, i.e. it is not + * possible that this flow is no longer busy before the handler is called. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param handler callback function. NULL to disable. + * @param user value passed to callback function. Ignored if handler is NULL. + */ +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user); + +/** + * Returns the input interface of the flow. + * + * @param flow the object + * @return input interface + */ +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h b/app/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h new file mode 100644 index 0000000..5dd0a7d --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h @@ -0,0 +1,7 @@ +#define SAVL_PARAM_NAME PacketPassFairQueue__Tree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct PacketPassFairQueueFlow_s +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_flows((entry1), (entry2)) +#define SAVL_PARAM_MEMBER_NODE queued.tree_node diff --git a/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.c b/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.c new file mode 100644 index 0000000..0395006 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.c @@ -0,0 +1,241 @@ +/** + * @file PacketPassFifoQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "PacketPassFifoQueue.h" + +static void schedule (PacketPassFifoQueue *o) +{ + ASSERT(!o->freeing) + ASSERT(!o->sending_flow) + ASSERT(!LinkedList1_IsEmpty(&o->waiting_flows_list)) + ASSERT(!BPending_IsSet(&o->schedule_job)) + + // get first waiting flow + PacketPassFifoQueueFlow *flow = UPPER_OBJECT(LinkedList1_GetFirst(&o->waiting_flows_list), PacketPassFifoQueueFlow, waiting_flows_list_node); + ASSERT(flow->queue == o) + ASSERT(flow->is_waiting) + + // remove it from queue + LinkedList1_Remove(&o->waiting_flows_list, &flow->waiting_flows_list_node); + flow->is_waiting = 0; + + // send + PacketPassInterface_Sender_Send(o->output, flow->waiting_data, flow->waiting_len); + o->sending_flow = flow; +} + +static void schedule_job_handler (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->freeing) + ASSERT(!o->sending_flow) + + if (!LinkedList1_IsEmpty(&o->waiting_flows_list)) { + schedule(o); + } +} + +static void input_handler_send (PacketPassFifoQueueFlow *o, uint8_t *data, int data_len) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Access(&o->d_obj); + ASSERT(!o->is_waiting) + ASSERT(o != queue->sending_flow) + ASSERT(!queue->freeing) + + // queue flow + o->waiting_data = data; + o->waiting_len = data_len; + LinkedList1_Append(&queue->waiting_flows_list, &o->waiting_flows_list_node); + o->is_waiting = 1; + + // schedule + if (!queue->sending_flow && !BPending_IsSet(&queue->schedule_job)) { + schedule(queue); + } +} + +static void output_handler_done (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sending_flow) + ASSERT(!BPending_IsSet(&o->schedule_job)) + ASSERT(!o->freeing) + ASSERT(!o->sending_flow->is_waiting) + + PacketPassFifoQueueFlow *flow = o->sending_flow; + + // set no sending flow + o->sending_flow = NULL; + + // schedule schedule job + BPending_Set(&o->schedule_job); + + // done input + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassFifoQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +void PacketPassFifoQueue_Init (PacketPassFifoQueue *o, PacketPassInterface *output, BPendingGroup *pg) +{ + // init arguments + o->output = output; + o->pg = pg; + + // init output + PacketPassInterface_Sender_Init(output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init waiting flows list + LinkedList1_Init(&o->waiting_flows_list); + + // set no sending flow + o->sending_flow = NULL; + + // init schedule job + BPending_Init(&o->schedule_job, pg, (BPending_handler)schedule_job_handler, o); + + // set not freeing + o->freeing = 0; + + DebugCounter_Init(&o->d_flows_ctr); + DebugObject_Init(&o->d_obj); +} + +void PacketPassFifoQueue_Free (PacketPassFifoQueue *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_flows_ctr); + ASSERT(LinkedList1_IsEmpty(&o->waiting_flows_list)) + ASSERT(!o->sending_flow) + + // free schedule job + BPending_Free(&o->schedule_job); +} + +void PacketPassFifoQueue_PrepareFree (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + + // set freeing + o->freeing = 1; +} + +void PacketPassFifoQueueFlow_Init (PacketPassFifoQueueFlow *o, PacketPassFifoQueue *queue) +{ + DebugObject_Access(&queue->d_obj); + ASSERT(!queue->freeing) + + // init arguments + o->queue = queue; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(queue->output), (PacketPassInterface_handler_send)input_handler_send, o, queue->pg); + + // set not waiting + o->is_waiting = 0; + + // set no busy handler + o->handler_busy = NULL; + + DebugCounter_Increment(&queue->d_flows_ctr); + DebugObject_Init(&o->d_obj); +} + +void PacketPassFifoQueueFlow_Free (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&queue->d_flows_ctr); + ASSERT(queue->freeing || o != queue->sending_flow) + + // remove from sending flow + if (o == queue->sending_flow) { + queue->sending_flow = NULL; + } + + // remove from waiting flows list + if (o->is_waiting) { + LinkedList1_Remove(&queue->waiting_flows_list, &o->waiting_flows_list_node); + } + + // free input + PacketPassInterface_Free(&o->input); +} + +void PacketPassFifoQueueFlow_AssertFree (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + B_USE(queue) + DebugObject_Access(&o->d_obj); + ASSERT(queue->freeing || o != queue->sending_flow) +} + +int PacketPassFifoQueueFlow_IsBusy (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Access(&o->d_obj); + ASSERT(!queue->freeing) + + return (o == queue->sending_flow); +} + +void PacketPassFifoQueueFlow_SetBusyHandler (PacketPassFifoQueueFlow *o, PacketPassFifoQueue_handler_busy handler_busy, void *user) +{ + PacketPassFifoQueue *queue = o->queue; + B_USE(queue) + DebugObject_Access(&o->d_obj); + ASSERT(!queue->freeing) + ASSERT(o == queue->sending_flow) + + // set (or unset) busy handler + o->handler_busy = handler_busy; + o->user = user; +} + +PacketPassInterface * PacketPassFifoQueueFlow_GetInput (PacketPassFifoQueueFlow *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.h b/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.h new file mode 100644 index 0000000..cefe79b --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassFifoQueue.h @@ -0,0 +1,76 @@ +/** + * @file PacketPassFifoQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PACKETPASSFIFOQUEUE_H +#define BADVPN_PACKETPASSFIFOQUEUE_H + +#include +#include +#include +#include + +typedef void (*PacketPassFifoQueue_handler_busy) (void *user); + +struct PacketPassFifoQueueFlow_s; + +typedef struct { + PacketPassInterface *output; + BPendingGroup *pg; + LinkedList1 waiting_flows_list; + struct PacketPassFifoQueueFlow_s *sending_flow; + BPending schedule_job; + int freeing; + DebugCounter d_flows_ctr; + DebugObject d_obj; +} PacketPassFifoQueue; + +typedef struct PacketPassFifoQueueFlow_s { + PacketPassFifoQueue *queue; + PacketPassInterface input; + int is_waiting; + LinkedList1Node waiting_flows_list_node; + uint8_t *waiting_data; + int waiting_len; + PacketPassFifoQueue_handler_busy handler_busy; + void *user; + DebugObject d_obj; +} PacketPassFifoQueueFlow; + +void PacketPassFifoQueue_Init (PacketPassFifoQueue *o, PacketPassInterface *output, BPendingGroup *pg); +void PacketPassFifoQueue_Free (PacketPassFifoQueue *o); +void PacketPassFifoQueue_PrepareFree (PacketPassFifoQueue *o); + +void PacketPassFifoQueueFlow_Init (PacketPassFifoQueueFlow *o, PacketPassFifoQueue *queue); +void PacketPassFifoQueueFlow_Free (PacketPassFifoQueueFlow *o); +void PacketPassFifoQueueFlow_AssertFree (PacketPassFifoQueueFlow *o); +int PacketPassFifoQueueFlow_IsBusy (PacketPassFifoQueueFlow *o); +void PacketPassFifoQueueFlow_SetBusyHandler (PacketPassFifoQueueFlow *o, PacketPassFifoQueue_handler_busy handler_busy, void *user); +PacketPassInterface * PacketPassFifoQueueFlow_GetInput (PacketPassFifoQueueFlow *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassInterface.c b/app/src/main/jni/badvpn/flow/PacketPassInterface.c new file mode 100644 index 0000000..dfa6ed5 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassInterface.c @@ -0,0 +1,68 @@ +/** + * @file PacketPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketPassInterface_job_operation (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + ASSERT(i->cancel_requested) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // call handler + i->handler_requestcancel(i->user_provider); + return; +} + +void _PacketPassInterface_job_done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user); + return; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassInterface.h b/app/src/main/jni/badvpn/flow/PacketPassInterface.h new file mode 100644 index 0000000..0cc2274 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassInterface.h @@ -0,0 +1,236 @@ +/** + * @file PacketPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet sender to pass data packets to a packet receiver. + */ + +#ifndef BADVPN_FLOW_PACKETPASSINTERFACE_H +#define BADVPN_FLOW_PACKETPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PPI_STATE_NONE 1 +#define PPI_STATE_OPERATION_PENDING 2 +#define PPI_STATE_BUSY 3 +#define PPI_STATE_DONE_PENDING 4 + +typedef void (*PacketPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*PacketPassInterface_handler_requestcancel) (void *user); + +typedef void (*PacketPassInterface_handler_done) (void *user); + +typedef struct { + // provider data + int mtu; + PacketPassInterface_handler_send handler_operation; + PacketPassInterface_handler_requestcancel handler_requestcancel; + void *user_provider; + + // user data + PacketPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // requestcancel job + BPending job_requestcancel; + + // done job + BPending job_done; + + // state + int state; + int cancel_requested; + + DebugObject d_obj; +} PacketPassInterface; + +static void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void PacketPassInterface_Free (PacketPassInterface *i); + +static void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel); + +static void PacketPassInterface_Done (PacketPassInterface *i); + +static int PacketPassInterface_GetMTU (PacketPassInterface *i); + +static void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user); + +static void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len); + +static void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i); + +static int PacketPassInterface_HasCancel (PacketPassInterface *i); + +void _PacketPassInterface_job_operation (PacketPassInterface *i); +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i); +void _PacketPassInterface_job_done (PacketPassInterface *i); + +void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->handler_requestcancel = NULL; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketPassInterface_job_operation, i); + BPending_Init(&i->job_requestcancel, pg, (BPending_handler)_PacketPassInterface_job_requestcancel, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketPassInterface_job_done, i); + + // set state + i->state = PPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketPassInterface_Free (PacketPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_requestcancel); + BPending_Free(&i->job_operation); +} + +void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel) +{ + ASSERT(!i->handler_requestcancel) + ASSERT(!i->handler_done) + ASSERT(handler_requestcancel) + + i->handler_requestcancel = handler_requestcancel; +} + +void PacketPassInterface_Done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // unset requestcancel job + BPending_Unset(&i->job_requestcancel); + + // schedule done + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; +} + +int PacketPassInterface_GetMTU (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(!(data_len > 0) || data) + ASSERT(i->state == PPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = PPI_STATE_OPERATION_PENDING; + i->cancel_requested = 0; +} + +void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING || i->state == PPI_STATE_BUSY || i->state == PPI_STATE_DONE_PENDING) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // ignore multiple cancel requests + if (i->cancel_requested) { + return; + } + + // remember we requested cancel + i->cancel_requested = 1; + + if (i->state == PPI_STATE_OPERATION_PENDING) { + // unset operation job + BPending_Unset(&i->job_operation); + + // set done job + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; + } else if (i->state == PPI_STATE_BUSY) { + // set requestcancel job + BPending_Set(&i->job_requestcancel); + } +} + +int PacketPassInterface_HasCancel (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return !!i->handler_requestcancel; +} + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassNotifier.c b/app/src/main/jni/badvpn/flow/PacketPassNotifier.c new file mode 100644 index 0000000..0132746 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassNotifier.c @@ -0,0 +1,103 @@ +/** + * @file PacketPassNotifier.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +void input_handler_send (PacketPassNotifier *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // schedule send + PacketPassInterface_Sender_Send(o->output, data, data_len); + + // if we have a handler, call it + if (o->handler) { + o->handler(o->handler_user, data, data_len); + return; + } +} + +void input_handler_requestcancel (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Sender_RequestCancel(o->output); +} + +void output_handler_done (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Done(&o->input); +} + +void PacketPassNotifier_Init (PacketPassNotifier *o, PacketPassInterface *output, BPendingGroup *pg) +{ + // init arguments + o->output = output; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, pg); + if (PacketPassInterface_HasCancel(o->output)) { + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + } + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // set no handler + o->handler = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketPassNotifier_Free (PacketPassNotifier *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassNotifier_GetInput (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassNotifier_SetHandler (PacketPassNotifier *o, PacketPassNotifier_handler_notify handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->handler_user = user; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassNotifier.h b/app/src/main/jni/badvpn/flow/PacketPassNotifier.h new file mode 100644 index 0000000..b2fab13 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassNotifier.h @@ -0,0 +1,99 @@ +/** + * @file PacketPassNotifier.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer which calles a handler function before + * passing a packet from input to output. + */ + +#ifndef BADVPN_FLOW_PACKETPASSNOTIFIER_H +#define BADVPN_FLOW_PACKETPASSNOTIFIER_H + +#include + +#include +#include + +/** + * Handler function called when input calls Send, but before the call is passed on to output. + * + * @param user value specified in {@link PacketPassNotifier_SetHandler} + * @param data packet provided by input + * @param data_len size of the packet + */ +typedef void (*PacketPassNotifier_handler_notify) (void *user, uint8_t *data, int data_len); + +/** + * A {@link PacketPassInterface} layer which calles a handler function before + * passing a packet from input to output. + */ +typedef struct { + PacketPassInterface input; + PacketPassInterface *output; + PacketPassNotifier_handler_notify handler; + void *handler_user; + DebugObject d_obj; +} PacketPassNotifier; + +/** + * Initializes the object. + * + * @param o the object + * @param output output interface + * @param pg pending group + */ +void PacketPassNotifier_Init (PacketPassNotifier *o, PacketPassInterface *output, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassNotifier_Free (PacketPassNotifier *o); + +/** + * Returns the input interface. + * The MTU of the interface will be the same as of the output interface. + * The interface supports cancel functionality if the output interface supports it. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassNotifier_GetInput (PacketPassNotifier *o); + +/** + * Configures a handler function to call before passing input packets to output. + * + * @param o the object + * @param handler handler function, or NULL to disable. + * @param user value to pass to handler function. Ignored if handler is NULL. + */ +void PacketPassNotifier_SetHandler (PacketPassNotifier *o, PacketPassNotifier_handler_notify handler, void *user); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.c b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.c new file mode 100644 index 0000000..9534f1b --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.c @@ -0,0 +1,283 @@ +/** + * @file PacketPassPriorityQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +static int compare_flows (PacketPassPriorityQueueFlow *f1, PacketPassPriorityQueueFlow *f2) +{ + int cmp = B_COMPARE(f1->priority, f2->priority); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)f1, (uintptr_t)f2); +} + +#include "PacketPassPriorityQueue_tree.h" +#include + +static void schedule (PacketPassPriorityQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + ASSERT(!PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) + + // get first queued flow + PacketPassPriorityQueueFlow *qflow = PacketPassPriorityQueue__Tree_GetFirst(&m->queued_tree, 0); + ASSERT(qflow->is_queued) + + // remove flow from queue + PacketPassPriorityQueue__Tree_Remove(&m->queued_tree, 0, qflow); + qflow->is_queued = 0; + + // schedule send + PacketPassInterface_Sender_Send(m->output, qflow->queued.data, qflow->queued.data_len); + m->sending_flow = qflow; +} + +static void schedule_job_handler (PacketPassPriorityQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + if (!PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) { + schedule(m); + } +} + +static void input_handler_send (PacketPassPriorityQueueFlow *flow, uint8_t *data, int data_len) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(flow != m->sending_flow) + ASSERT(!flow->is_queued) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // queue flow + flow->queued.data = data; + flow->queued.data_len = data_len; + int res = PacketPassPriorityQueue__Tree_Insert(&m->queued_tree, 0, flow, NULL); + ASSERT_EXECUTE(res) + flow->is_queued = 1; + + if (!m->sending_flow && !BPending_IsSet(&m->schedule_job)) { + schedule(m); + } +} + +static void output_handler_done (PacketPassPriorityQueue *m) +{ + ASSERT(m->sending_flow) + ASSERT(!BPending_IsSet(&m->schedule_job)) + ASSERT(!m->freeing) + ASSERT(!m->sending_flow->is_queued) + + PacketPassPriorityQueueFlow *flow = m->sending_flow; + + // sending finished + m->sending_flow = NULL; + + // schedule schedule + BPending_Set(&m->schedule_job); + + // finish flow packet + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassPriorityQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +void PacketPassPriorityQueue_Init (PacketPassPriorityQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel) +{ + ASSERT(use_cancel == 0 || use_cancel == 1) + ASSERT(!use_cancel || PacketPassInterface_HasCancel(output)) + + // init arguments + m->output = output; + m->pg = pg; + m->use_cancel = use_cancel; + + // init output + PacketPassInterface_Sender_Init(m->output, (PacketPassInterface_handler_done)output_handler_done, m); + + // not sending + m->sending_flow = NULL; + + // init queued tree + PacketPassPriorityQueue__Tree_Init(&m->queued_tree); + + // not freeing + m->freeing = 0; + + // init schedule job + BPending_Init(&m->schedule_job, m->pg, (BPending_handler)schedule_job_handler, m); + + DebugObject_Init(&m->d_obj); + DebugCounter_Init(&m->d_ctr); +} + +void PacketPassPriorityQueue_Free (PacketPassPriorityQueue *m) +{ + ASSERT(PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) + ASSERT(!m->sending_flow) + DebugCounter_Free(&m->d_ctr); + DebugObject_Free(&m->d_obj); + + // free schedule job + BPending_Free(&m->schedule_job); +} + +void PacketPassPriorityQueue_PrepareFree (PacketPassPriorityQueue *m) +{ + DebugObject_Access(&m->d_obj); + + // set freeing + m->freeing = 1; +} + +int PacketPassPriorityQueue_GetMTU (PacketPassPriorityQueue *m) +{ + DebugObject_Access(&m->d_obj); + + return PacketPassInterface_GetMTU(m->output); +} + +void PacketPassPriorityQueueFlow_Init (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue *m, int priority) +{ + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // init arguments + flow->m = m; + flow->priority = priority; + + // have no canfree handler + flow->handler_busy = NULL; + + // init input + PacketPassInterface_Init(&flow->input, PacketPassInterface_GetMTU(flow->m->output), (PacketPassInterface_handler_send)input_handler_send, flow, m->pg); + + // is not queued + flow->is_queued = 0; + + DebugObject_Init(&flow->d_obj); + DebugCounter_Increment(&m->d_ctr); +} + +void PacketPassPriorityQueueFlow_Free (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(m->freeing || flow != m->sending_flow) + DebugCounter_Decrement(&m->d_ctr); + DebugObject_Free(&flow->d_obj); + + // remove from current flow + if (flow == m->sending_flow) { + m->sending_flow = NULL; + } + + // remove from queue + if (flow->is_queued) { + PacketPassPriorityQueue__Tree_Remove(&m->queued_tree, 0, flow); + } + + // free input + PacketPassInterface_Free(&flow->input); +} + +void PacketPassPriorityQueueFlow_AssertFree (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + B_USE(m) + + ASSERT(m->freeing || flow != m->sending_flow) + DebugObject_Access(&flow->d_obj); +} + +int PacketPassPriorityQueueFlow_IsBusy (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + return (flow == m->sending_flow); +} + +void PacketPassPriorityQueueFlow_RequestCancel (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(flow == m->sending_flow) + ASSERT(m->use_cancel) + ASSERT(!m->freeing) + ASSERT(!BPending_IsSet(&m->schedule_job)) + DebugObject_Access(&flow->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(m->output); +} + +void PacketPassPriorityQueueFlow_SetBusyHandler (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue_handler_busy handler, void *user) +{ + PacketPassPriorityQueue *m = flow->m; + B_USE(m) + + ASSERT(flow == m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // set handler + flow->handler_busy = handler; + flow->user = user; +} + +PacketPassInterface * PacketPassPriorityQueueFlow_GetInput (PacketPassPriorityQueueFlow *flow) +{ + DebugObject_Access(&flow->d_obj); + + return &flow->input; +} diff --git a/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.h b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.h new file mode 100644 index 0000000..3ac78eb --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue.h @@ -0,0 +1,192 @@ +/** + * @file PacketPassPriorityQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Priority queue using {@link PacketPassInterface}. + */ + +#ifndef BADVPN_FLOW_PACKETPASSPRIORITYQUEUE_H +#define BADVPN_FLOW_PACKETPASSPRIORITYQUEUE_H + +#include + +#include +#include +#include +#include +#include + +typedef void (*PacketPassPriorityQueue_handler_busy) (void *user); + +struct PacketPassPriorityQueueFlow_s; + +#include "PacketPassPriorityQueue_tree.h" +#include + +typedef struct PacketPassPriorityQueueFlow_s { + struct PacketPassPriorityQueue_s *m; + int priority; + PacketPassPriorityQueue_handler_busy handler_busy; + void *user; + PacketPassInterface input; + int is_queued; + struct { + PacketPassPriorityQueue__TreeNode tree_node; + uint8_t *data; + int data_len; + } queued; + DebugObject d_obj; +} PacketPassPriorityQueueFlow; + +/** + * Priority queue using {@link PacketPassInterface}. + */ +typedef struct PacketPassPriorityQueue_s { + PacketPassInterface *output; + BPendingGroup *pg; + int use_cancel; + struct PacketPassPriorityQueueFlow_s *sending_flow; + PacketPassPriorityQueue__Tree queued_tree; + int freeing; + BPending schedule_job; + DebugObject d_obj; + DebugCounter d_ctr; +} PacketPassPriorityQueue; + +/** + * Initializes the queue. + * + * @param m the object + * @param output output interface + * @param pg pending group + * @param use_cancel whether cancel functionality is required. Must be 0 or 1. + * If 1, output must support cancel functionality. + */ +void PacketPassPriorityQueue_Init (PacketPassPriorityQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel); + +/** + * Frees the queue. + * All flows must have been freed. + * + * @param m the object + */ +void PacketPassPriorityQueue_Free (PacketPassPriorityQueue *m); + +/** + * Prepares for freeing the entire queue. Must be called to allow freeing + * the flows in the process of freeing the entire queue. + * After this function is called, flows and the queue must be freed + * before any further I/O. + * May be called multiple times. + * The queue enters freeing state. + * + * @param m the object + */ +void PacketPassPriorityQueue_PrepareFree (PacketPassPriorityQueue *m); + +/** + * Returns the MTU of the queue. + * + * @param m the object + */ +int PacketPassPriorityQueue_GetMTU (PacketPassPriorityQueue *m); + +/** + * Initializes a queue flow. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param m queue to attach to + * @param priority flow priority. Lower value means higher priority. + */ +void PacketPassPriorityQueueFlow_Init (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue *m, int priority); + +/** + * Frees a queue flow. + * Unless the queue is in freeing state: + * - The flow must not be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * - Must not be called from queue calls to output. + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_Free (PacketPassPriorityQueueFlow *flow); + +/** + * Does nothing. + * It must be possible to free the flow (see {@link PacketPassPriorityQueueFlow}). + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_AssertFree (PacketPassPriorityQueueFlow *flow); + +/** + * Determines if the flow is busy. If the flow is considered busy, it must not + * be freed. At any given time, at most one flow will be indicated as busy. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @return 0 if not busy, 1 is busy + */ +int PacketPassPriorityQueueFlow_IsBusy (PacketPassPriorityQueueFlow *flow); + +/** + * Requests the output to stop processing the current packet as soon as possible. + * Cancel functionality must be enabled for the queue. + * The flow must be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_RequestCancel (PacketPassPriorityQueueFlow *flow); + +/** + * Sets up a callback to be called when the flow is no longer busy. + * The handler will be called as soon as the flow is no longer busy, i.e. it is not + * possible that this flow is no longer busy before the handler is called. + * The flow must be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param handler callback function. NULL to disable. + * @param user value passed to callback function. Ignored if handler is NULL. + */ +void PacketPassPriorityQueueFlow_SetBusyHandler (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue_handler_busy handler, void *user); + +/** + * Returns the input interface of the flow. + * + * @param flow the object + * @return input interface + */ +PacketPassInterface * PacketPassPriorityQueueFlow_GetInput (PacketPassPriorityQueueFlow *flow); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue_tree.h b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue_tree.h new file mode 100644 index 0000000..0d8b213 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketPassPriorityQueue_tree.h @@ -0,0 +1,7 @@ +#define SAVL_PARAM_NAME PacketPassPriorityQueue__Tree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct PacketPassPriorityQueueFlow_s +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_flows((entry1), (entry2)) +#define SAVL_PARAM_MEMBER_NODE queued.tree_node diff --git a/app/src/main/jni/badvpn/flow/PacketProtoDecoder.c b/app/src/main/jni/badvpn/flow/PacketProtoDecoder.c new file mode 100644 index 0000000..68d26c5 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoDecoder.c @@ -0,0 +1,182 @@ +/** + * @file PacketProtoDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static void process_data (PacketProtoDecoder *enc); +static void input_handler_done (PacketProtoDecoder *enc, int data_len); +static void output_handler_done (PacketProtoDecoder *enc); + +void process_data (PacketProtoDecoder *enc) +{ + int was_error = 0; + + do { + uint8_t *data = enc->buf + enc->buf_start; + int left = enc->buf_used; + + // check if header was received + if (left < sizeof(struct packetproto_header)) { + break; + } + struct packetproto_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(struct packetproto_header); + left -= sizeof(struct packetproto_header); + int data_len = ltoh16(header.len); + + // check data length + if (data_len > enc->output_mtu) { + BLog(BLOG_NOTICE, "error: packet too large"); + was_error = 1; + break; + } + + // check if whole packet was received + if (left < data_len) { + break; + } + + // update buffer + enc->buf_start += sizeof(struct packetproto_header) + data_len; + enc->buf_used -= sizeof(struct packetproto_header) + data_len; + + // submit packet + PacketPassInterface_Sender_Send(enc->output, data, data_len); + return; + } while (0); + + if (was_error) { + // reset buffer + enc->buf_start = 0; + enc->buf_used = 0; + } else { + // if we reached the end of the buffer, wrap around to allow more data to be received + if (enc->buf_start + enc->buf_used == enc->buf_size) { + memmove(enc->buf, enc->buf + enc->buf_start, enc->buf_used); + enc->buf_start = 0; + } + } + + // receive data + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf + (enc->buf_start + enc->buf_used), enc->buf_size - (enc->buf_start + enc->buf_used)); + + // if we had error, report it + if (was_error) { + enc->handler_error(enc->user); + return; + } +} + +static void input_handler_done (PacketProtoDecoder *enc, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= enc->buf_size - (enc->buf_start + enc->buf_used)) + DebugObject_Access(&enc->d_obj); + + // update buffer + enc->buf_used += data_len; + + // process data + process_data(enc); + return; +} + +void output_handler_done (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + // process data + process_data(enc); + return; +} + +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) +{ + // init arguments + enc->input = input; + enc->output = output; + enc->user = user; + enc->handler_error = handler_error; + + // init input + StreamRecvInterface_Receiver_Init(enc->input, (StreamRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketPassInterface_Sender_Init(enc->output, (PacketPassInterface_handler_done)output_handler_done, enc); + + // set output MTU, limit by maximum payload size + enc->output_mtu = bmin_int(PacketPassInterface_GetMTU(enc->output), PACKETPROTO_MAXPAYLOAD); + + // init buffer state + enc->buf_size = PACKETPROTO_ENCLEN(enc->output_mtu); + enc->buf_start = 0; + enc->buf_used = 0; + + // allocate buffer + if (!(enc->buf = (uint8_t *)malloc(enc->buf_size))) { + goto fail0; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf, enc->buf_size); + + DebugObject_Init(&enc->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketProtoDecoder_Free (PacketProtoDecoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free buffer + free(enc->buf); +} + +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + enc->buf_start += enc->buf_used; + enc->buf_used = 0; +} diff --git a/app/src/main/jni/badvpn/flow/PacketProtoDecoder.h b/app/src/main/jni/badvpn/flow/PacketProtoDecoder.h new file mode 100644 index 0000000..2c20694 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoDecoder.h @@ -0,0 +1,96 @@ +/** + * @file PacketProtoDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes a stream according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTODECODER_H +#define BADVPN_FLOW_PACKETPROTODECODER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Handler called when a protocol error occurs. + * When an error occurs, the decoder is reset to the initial state. + * + * @param user as in {@link PacketProtoDecoder_Init} + */ +typedef void (*PacketProtoDecoder_handler_error) (void *user); + +typedef struct { + StreamRecvInterface *input; + PacketPassInterface *output; + void *user; + PacketProtoDecoder_handler_error handler_error; + int output_mtu; + int buf_size; + int buf_start; + int buf_used; + uint8_t *buf; + DebugObject d_obj; +} PacketProtoDecoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. The decoder will accept packets with payload size up to its MTU + * (but the payload can never be more than PACKETPROTO_MAXPAYLOAD). + * @param output output interface + * @param pg pending group + * @param user argument to handlers + * @param handler_error error handler + * @return 1 on success, 0 on failure + */ +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) WARN_UNUSED; + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoDecoder_Free (PacketProtoDecoder *enc); + +/** + * Clears the internal buffer. + * The next data received from the input will be treated as a new + * PacketProto stream. + * + * @param enc the object + */ +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketProtoEncoder.c b/app/src/main/jni/badvpn/flow/PacketProtoEncoder.c new file mode 100644 index 0000000..00aaa95 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoEncoder.c @@ -0,0 +1,101 @@ +/** + * @file PacketProtoEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +static void output_handler_recv (PacketProtoEncoder *enc, uint8_t *data) +{ + ASSERT(!enc->output_packet) + ASSERT(data) + DebugObject_Access(&enc->d_obj); + + // schedule receive + enc->output_packet = data; + PacketRecvInterface_Receiver_Recv(enc->input, enc->output_packet + sizeof(struct packetproto_header)); +} + +static void input_handler_done (PacketProtoEncoder *enc, int in_len) +{ + ASSERT(enc->output_packet) + DebugObject_Access(&enc->d_obj); + + // write length + struct packetproto_header pp; + pp.len = htol16(in_len); + memcpy(enc->output_packet, &pp, sizeof(pp)); + + // finish output packet + enc->output_packet = NULL; + PacketRecvInterface_Done(&enc->output, PACKETPROTO_ENCLEN(in_len)); +} + +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= PACKETPROTO_MAXPAYLOAD) + + // init arguments + enc->input = input; + + // init input + PacketRecvInterface_Receiver_Init(enc->input, (PacketRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketRecvInterface_Init( + &enc->output, PACKETPROTO_ENCLEN(PacketRecvInterface_GetMTU(enc->input)), + (PacketRecvInterface_handler_recv)output_handler_recv, enc, pg + ); + + // set no output packet + enc->output_packet = NULL; + + DebugObject_Init(&enc->d_obj); +} + +void PacketProtoEncoder_Free (PacketProtoEncoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free input + PacketRecvInterface_Free(&enc->output); +} + +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + return &enc->output; +} diff --git a/app/src/main/jni/badvpn/flow/PacketProtoEncoder.h b/app/src/main/jni/badvpn/flow/PacketProtoEncoder.h new file mode 100644 index 0000000..022aa00 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoEncoder.h @@ -0,0 +1,80 @@ +/** + * @file PacketProtoEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOENCODER_H +#define BADVPN_FLOW_PACKETPROTOENCODER_H + +#include + +#include +#include + +/** + * Object which encodes packets according to PacketProto. + * + * Input is with {@link PacketRecvInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *output_packet; + DebugObject d_obj; +} PacketProtoEncoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. Its MTU must be <=PACKETPROTO_MAXPAYLOAD. + * @param pg pending group + */ +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoEncoder_Free (PacketProtoEncoder *enc); + +/** + * Returns the output interface. + * The MTU of the output interface is PACKETPROTO_ENCLEN(MTU of input interface). + * + * @param enc the object + * @return output interface + */ +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketProtoFlow.c b/app/src/main/jni/badvpn/flow/PacketProtoFlow.c new file mode 100644 index 0000000..8fad8e2 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoFlow.c @@ -0,0 +1,82 @@ +/** + * @file PacketProtoFlow.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(input_mtu >= 0) + ASSERT(input_mtu <= PACKETPROTO_MAXPAYLOAD) + ASSERT(num_packets > 0) + ASSERT(PacketPassInterface_GetMTU(output) >= PACKETPROTO_ENCLEN(input_mtu)) + + // init async input + BufferWriter_Init(&o->ainput, input_mtu, pg); + + // init encoder + PacketProtoEncoder_Init(&o->encoder, BufferWriter_GetOutput(&o->ainput), pg); + + // init buffer + if (!PacketBuffer_Init(&o->buffer, PacketProtoEncoder_GetOutput(&o->encoder), output, num_packets, pg)) { + goto fail0; + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + PacketProtoEncoder_Free(&o->encoder); + BufferWriter_Free(&o->ainput); + return 0; +} + +void PacketProtoFlow_Free (PacketProtoFlow *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + PacketBuffer_Free(&o->buffer); + + // free encoder + PacketProtoEncoder_Free(&o->encoder); + + // free async input + BufferWriter_Free(&o->ainput); +} + +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->ainput; +} diff --git a/app/src/main/jni/badvpn/flow/PacketProtoFlow.h b/app/src/main/jni/badvpn/flow/PacketProtoFlow.h new file mode 100644 index 0000000..05e1233 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketProtoFlow.h @@ -0,0 +1,83 @@ +/** + * @file PacketProtoFlow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOFLOW_H +#define BADVPN_FLOW_PACKETPROTOFLOW_H + +#include + +#include +#include +#include +#include + +/** + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ +typedef struct { + BufferWriter ainput; + PacketProtoEncoder encoder; + PacketBuffer buffer; + DebugObject d_obj; +} PacketProtoFlow; + +/** + * Initializes the object. + * + * @param o the object + * @param input_mtu maximum input packet size. Must be >=0 and <=PACKETPROTO_MAXPAYLOAD. + * @param num_packets minimum number of packets the buffer should hold. Must be >0. + * @param output output interface. Its MTU must be >=PACKETPROTO_ENCLEN(input_mtu). + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void PacketProtoFlow_Free (PacketProtoFlow *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketRecvBlocker.c b/app/src/main/jni/badvpn/flow/PacketRecvBlocker.c new file mode 100644 index 0000000..7e679f8 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvBlocker.c @@ -0,0 +1,99 @@ +/** + * @file PacketRecvBlocker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void output_handler_recv (PacketRecvBlocker *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // remember packet + o->out_have = 1; + o->out = data; + o->out_input_blocking = 0; +} + +static void input_handler_done (PacketRecvBlocker *o, int data_len) +{ + ASSERT(o->out_have) + ASSERT(o->out_input_blocking) + DebugObject_Access(&o->d_obj); + + // schedule done + o->out_have = 0; + PacketRecvInterface_Done(&o->output, data_len); +} + +void PacketRecvBlocker_Init (PacketRecvBlocker *o, PacketRecvInterface *input, BPendingGroup *pg) +{ + // init arguments + o->input = input; + + // init output + PacketRecvInterface_Init(&o->output, PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_have = 0; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + DebugObject_Init(&o->d_obj); +} + +void PacketRecvBlocker_Free (PacketRecvBlocker *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * PacketRecvBlocker_GetOutput (PacketRecvBlocker *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void PacketRecvBlocker_AllowBlockedPacket (PacketRecvBlocker *o) +{ + DebugObject_Access(&o->d_obj); + + if (!o->out_have || o->out_input_blocking) { + return; + } + + // schedule receive + o->out_input_blocking = 1; + PacketRecvInterface_Receiver_Recv(o->input, o->out); +} diff --git a/app/src/main/jni/badvpn/flow/PacketRecvBlocker.h b/app/src/main/jni/badvpn/flow/PacketRecvBlocker.h new file mode 100644 index 0000000..ba98d06 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvBlocker.h @@ -0,0 +1,90 @@ +/** + * @file PacketRecvBlocker.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link PacketRecvInterface} layer which blocks all output recv calls and only + * passes a single blocked call on to input when the user wants so. + */ + +#ifndef BADVPN_FLOW_PACKETRECVBLOCKER_H +#define BADVPN_FLOW_PACKETRECVBLOCKER_H + +#include + +#include +#include + +/** + * {@link PacketRecvInterface} layer which blocks all output recv calls and only + * passes a single blocked call on to input when the user wants so. + */ +typedef struct { + PacketRecvInterface output; + int out_have; + uint8_t *out; + int out_input_blocking; + PacketRecvInterface *input; + DebugObject d_obj; +} PacketRecvBlocker; + +/** + * Initializes the object. + * + * @param o the object + * @param input input interface + */ +void PacketRecvBlocker_Init (PacketRecvBlocker *o, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRecvBlocker_Free (PacketRecvBlocker *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be the same as of the input interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * PacketRecvBlocker_GetOutput (PacketRecvBlocker *o); + +/** + * Passes a blocked output recv call to input if there is one and it has not + * been passed yet. Otherwise it does nothing. + * Must not be called from input Recv calls. + * This function may invoke I/O. + * + * @param o the object + */ +void PacketRecvBlocker_AllowBlockedPacket (PacketRecvBlocker *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketRecvConnector.c b/app/src/main/jni/badvpn/flow/PacketRecvConnector.c new file mode 100644 index 0000000..455db23 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvConnector.c @@ -0,0 +1,123 @@ +/** + * @file PacketRecvConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void output_handler_recv (PacketRecvConnector *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_have = 1; + o->out = data; + + if (o->input) { + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, o->out); + } +} + +static void input_handler_done (PacketRecvConnector *o, int data_len) +{ + ASSERT(o->out_have) + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // have no output packet + o->out_have = 0; + + // allow output to receive more packets + PacketRecvInterface_Done(&o->output, data_len); +} + +void PacketRecvConnector_Init (PacketRecvConnector *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + o->output_mtu = mtu; + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_have = 0; + + // have no input + o->input = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketRecvConnector_Free (PacketRecvConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * PacketRecvConnector_GetOutput (PacketRecvConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void PacketRecvConnector_ConnectInput (PacketRecvConnector *o, PacketRecvInterface *input) +{ + ASSERT(!o->input) + ASSERT(PacketRecvInterface_GetMTU(input) <= o->output_mtu) + DebugObject_Access(&o->d_obj); + + // set input + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // if we have an output packet, schedule receive + if (o->out_have) { + PacketRecvInterface_Receiver_Recv(o->input, o->out); + } +} + +void PacketRecvConnector_DisconnectInput (PacketRecvConnector *o) +{ + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // set no input + o->input = NULL; +} diff --git a/app/src/main/jni/badvpn/flow/PacketRecvConnector.h b/app/src/main/jni/badvpn/flow/PacketRecvConnector.h new file mode 100644 index 0000000..25cf851 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvConnector.h @@ -0,0 +1,102 @@ +/** + * @file PacketRecvConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_PACKETRECVCONNECTOR_H +#define BADVPN_FLOW_PACKETRECVCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link PacketRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ +typedef struct { + PacketRecvInterface output; + int output_mtu; + int out_have; + uint8_t *out; + PacketRecvInterface *input; + DebugObject d_obj; +} PacketRecvConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param mtu maximum output packet size. Must be >=0. + * @param pg pending group + */ +void PacketRecvConnector_Init (PacketRecvConnector *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRecvConnector_Free (PacketRecvConnector *o); + +/** + * Returns the output interface. + * The MTU of the interface will be as in {@link PacketRecvConnector_Init}. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * PacketRecvConnector_GetOutput (PacketRecvConnector *o); + +/** + * Connects input. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output input to connect. Its MTU must be <= MTU specified in + * {@link PacketRecvConnector_Init}. + */ +void PacketRecvConnector_ConnectInput (PacketRecvConnector *o, PacketRecvInterface *input); + +/** + * Disconnects input. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void PacketRecvConnector_DisconnectInput (PacketRecvConnector *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketRecvInterface.c b/app/src/main/jni/badvpn/flow/PacketRecvInterface.c new file mode 100644 index 0000000..40bb8c6 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file PacketRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data); + return; +} + +void _PacketRecvInterface_job_done (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/app/src/main/jni/badvpn/flow/PacketRecvInterface.h b/app/src/main/jni/badvpn/flow/PacketRecvInterface.h new file mode 100644 index 0000000..6350ed2 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRecvInterface.h @@ -0,0 +1,170 @@ +/** + * @file PacketRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet receiver to receive data packets from a packet sender. + */ + +#ifndef BADVPN_FLOW_PACKETRECVINTERFACE_H +#define BADVPN_FLOW_PACKETRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PRI_STATE_NONE 1 +#define PRI_STATE_OPERATION_PENDING 2 +#define PRI_STATE_BUSY 3 +#define PRI_STATE_DONE_PENDING 4 + +typedef void (*PacketRecvInterface_handler_recv) (void *user, uint8_t *data); + +typedef void (*PacketRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + int mtu; + PacketRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + PacketRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} PacketRecvInterface; + +static void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void PacketRecvInterface_Free (PacketRecvInterface *i); + +static void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len); + +static int PacketRecvInterface_GetMTU (PacketRecvInterface *i); + +static void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user); + +static void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data); + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i); +void _PacketRecvInterface_job_done (PacketRecvInterface *i); + +void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketRecvInterface_job_done, i); + + // set state + i->state = PRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketRecvInterface_Free (PacketRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(i->state == PRI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = PRI_STATE_DONE_PENDING; +} + +int PacketRecvInterface_GetMTU (PacketRecvInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data) +{ + ASSERT(!(i->mtu > 0) || data) + ASSERT(i->state == PRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + BPending_Set(&i->job_operation); + + // set state + i->state = PRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketRouter.c b/app/src/main/jni/badvpn/flow/PacketRouter.c new file mode 100644 index 0000000..7b9f5f9 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRouter.c @@ -0,0 +1,129 @@ +/** + * @file PacketRouter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +static void input_handler_done (PacketRouter *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->mtu - o->recv_offset) + ASSERT(!BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); + + // set next job + BPending_Set(&o->next_job); + + // call handler + o->handler(o->user, RouteBufferSource_Pointer(&o->rbs), data_len); + return; +} + +static void next_job_handler (PacketRouter *o) +{ + DebugObject_Access(&o->d_obj); + + // receive + PacketRecvInterface_Receiver_Recv(o->input, RouteBufferSource_Pointer(&o->rbs) + o->recv_offset); +} + +int PacketRouter_Init (PacketRouter *o, int mtu, int recv_offset, PacketRecvInterface *input, PacketRouter_handler handler, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + ASSERT(recv_offset >= 0) + ASSERT(recv_offset <= mtu) + ASSERT(PacketRecvInterface_GetMTU(input) <= mtu - recv_offset) + + // init arguments + o->mtu = mtu; + o->recv_offset = recv_offset; + o->input = input; + o->handler = handler; + o->user = user; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init RouteBufferSource + if (!RouteBufferSource_Init(&o->rbs, mtu)) { + goto fail0; + } + + // init next job + BPending_Init(&o->next_job, pg, (BPending_handler)next_job_handler, o); + + // receive + PacketRecvInterface_Receiver_Recv(o->input, RouteBufferSource_Pointer(&o->rbs) + o->recv_offset); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketRouter_Free (PacketRouter *o) +{ + DebugObject_Free(&o->d_obj); + + // free next job + BPending_Free(&o->next_job); + + // free RouteBufferSource + RouteBufferSource_Free(&o->rbs); +} + +int PacketRouter_Route (PacketRouter *o, int len, RouteBuffer *output, uint8_t **next_buf, int copy_offset, int copy_len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->mtu) + ASSERT(RouteBuffer_GetMTU(output) == o->mtu) + ASSERT(copy_offset >= 0) + ASSERT(copy_offset <= o->mtu) + ASSERT(copy_len >= 0) + ASSERT(copy_len <= o->mtu - copy_offset) + ASSERT(BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); + + if (!RouteBufferSource_Route(&o->rbs, len, output, copy_offset, copy_len)) { + return 0; + } + + if (next_buf) { + *next_buf = RouteBufferSource_Pointer(&o->rbs); + } + + return 1; +} + +void PacketRouter_AssertRoute (PacketRouter *o) +{ + ASSERT(BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); +} diff --git a/app/src/main/jni/badvpn/flow/PacketRouter.h b/app/src/main/jni/badvpn/flow/PacketRouter.h new file mode 100644 index 0000000..3226078 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketRouter.h @@ -0,0 +1,126 @@ +/** + * @file PacketRouter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which simplifies routing packets to {@link RouteBuffer}'s from a + * {@link PacketRecvInterface} input. + */ + +#ifndef BADVPN_FLOW_PACKETROUTER_H +#define BADVPN_FLOW_PACKETROUTER_H + +#include +#include +#include +#include + +/** + * Handler called when a packet is received, allowing the user to route it + * to one or more buffers using {@link PacketRouter_Route}. + * + * @param user as in {@link PacketRouter_Init} + * @param buf the buffer for the packet. May be modified by the user. + * Will have space for mtu bytes. Only valid in the job context of + * this handler, until {@link PacketRouter_Route} is called successfully. + * @param recv_len length of the input packet (located at recv_offset bytes offset) + */ +typedef void (*PacketRouter_handler) (void *user, uint8_t *buf, int recv_len); + +/** + * Object which simplifies routing packets to {@link RouteBuffer}'s from a + * {@link PacketRecvInterface} input. + * + * Packets are routed by calling {@link PacketRouter_Route} (possibly multiple times) + * from the job context of the {@link PacketRouter_handler} handler. + */ +typedef struct { + int mtu; + int recv_offset; + PacketRecvInterface *input; + PacketRouter_handler handler; + void *user; + RouteBufferSource rbs; + BPending next_job; + DebugObject d_obj; +} PacketRouter; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. It will only be possible to route packets to + * {@link RouteBuffer}'s with the same MTU. + * @param recv_offset offset from the beginning for receiving input packets. + * Must be >=0 and <=mtu. The leading space should be initialized + * by the user before routing a packet. + * @param input input interface. Its MTU must be <= mtu - recv_offset. + * @param handler handler called when a packet is received to allow the user to route it + * @param user value passed to handler + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketRouter_Init (PacketRouter *o, int mtu, int recv_offset, PacketRecvInterface *input, PacketRouter_handler handler, void *user, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRouter_Free (PacketRouter *o); + +/** + * Routes the current packet to the given buffer. + * Must be called from the job context of the {@link PacketRouter_handler} handler. + * On success, copies part of the current packet to next one (regardless if next_buf + * is provided or not; if not, copies before receiving another packet). + * + * @param o the object + * @param len total packet length (e.g. recv_offset + (recv_len from handler)). + * Must be >=0 and <=mtu. + * @param output buffer to route to. Its MTU must be the same as of this object. + * @param next_buf if not NULL, on success, will be set to the address of a new current + * packet that can be routed. The pointer will be valid in the job context of + * the calling handler, until this function is called successfully again + * (as for the original pointer provided by the handler). + * @param copy_offset Offset from the beginning for copying to the next packet. + * Must be >=0 and <=mtu. + * @param copy_len Number of bytes to copy from the old current + * packet to the next one. Must be >=0 and <= mtu - copy_offset. + * @return 1 on success, 0 on failure (buffer full) + */ +int PacketRouter_Route (PacketRouter *o, int len, RouteBuffer *output, uint8_t **next_buf, int copy_offset, int copy_len); + +/** + * Asserts that {@link PacketRouter_Route} can be called. + * + * @param o the object + */ +void PacketRouter_AssertRoute (PacketRouter *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/PacketStreamSender.c b/app/src/main/jni/badvpn/flow/PacketStreamSender.c new file mode 100644 index 0000000..153a72b --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketStreamSender.c @@ -0,0 +1,111 @@ +/** + * @file PacketStreamSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void send_data (PacketStreamSender *s) +{ + ASSERT(s->in_len >= 0) + + if (s->in_used < s->in_len) { + // send more data + StreamPassInterface_Sender_Send(s->output, s->in + s->in_used, s->in_len - s->in_used); + } else { + // finish input packet + s->in_len = -1; + PacketPassInterface_Done(&s->input); + } +} + +static void input_handler_send (PacketStreamSender *s, uint8_t *data, int data_len) +{ + ASSERT(s->in_len == -1) + ASSERT(data_len >= 0) + DebugObject_Access(&s->d_obj); + + // set input packet + s->in_len = data_len; + s->in = data; + s->in_used = 0; + + // send + send_data(s); +} + +static void output_handler_done (PacketStreamSender *s, int data_len) +{ + ASSERT(s->in_len >= 0) + ASSERT(data_len > 0) + ASSERT(data_len <= s->in_len - s->in_used) + DebugObject_Access(&s->d_obj); + + // update number of bytes sent + s->in_used += data_len; + + // send + send_data(s); +} + +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + s->output = output; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)input_handler_send, s, pg); + + // init output + StreamPassInterface_Sender_Init(s->output, (StreamPassInterface_handler_done)output_handler_done, s); + + // have no input packet + s->in_len = -1; + + DebugObject_Init(&s->d_obj); +} + +void PacketStreamSender_Free (PacketStreamSender *s) +{ + DebugObject_Free(&s->d_obj); + + // free input + PacketPassInterface_Free(&s->input); +} + +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s) +{ + DebugObject_Access(&s->d_obj); + + return &s->input; +} diff --git a/app/src/main/jni/badvpn/flow/PacketStreamSender.h b/app/src/main/jni/badvpn/flow/PacketStreamSender.h new file mode 100644 index 0000000..735360c --- /dev/null +++ b/app/src/main/jni/badvpn/flow/PacketStreamSender.h @@ -0,0 +1,83 @@ +/** + * @file PacketStreamSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ + +#ifndef BADVPN_FLOW_PACKETSTREAMSENDER_H +#define BADVPN_FLOW_PACKETSTREAMSENDER_H + +#include + +#include +#include +#include + +/** + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface input; + StreamPassInterface *output; + int in_len; + uint8_t *in; + int in_used; +} PacketStreamSender; + +/** + * Initializes the object. + * + * @param s the object + * @param output output interface + * @param mtu input MTU. Must be >=0. + * @param pg pending group + */ +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param s the object + */ +void PacketStreamSender_Free (PacketStreamSender *s); + +/** + * Returns the input interface. + * Its MTU will be as in {@link PacketStreamSender_Init}. + * + * @param s the object + * @return input interface + */ +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s); + +#endif diff --git a/app/src/main/jni/badvpn/flow/RouteBuffer.c b/app/src/main/jni/badvpn/flow/RouteBuffer.c new file mode 100644 index 0000000..dec7be4 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/RouteBuffer.c @@ -0,0 +1,256 @@ +/** + * @file RouteBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include + +static struct RouteBuffer_packet * alloc_packet (int mtu) +{ + if (mtu > SIZE_MAX - sizeof(struct RouteBuffer_packet)) { + return NULL; + } + + // allocate memory + struct RouteBuffer_packet *p = (struct RouteBuffer_packet *)malloc(sizeof(*p) + mtu); + if (!p) { + return NULL; + } + + return p; +} + +static int alloc_free_packet (RouteBuffer *o) +{ + struct RouteBuffer_packet *p = alloc_packet(o->mtu); + if (!p) { + return 0; + } + + // add to free packets list + LinkedList1_Append(&o->packets_free, &p->node); + + return 1; +} + +static void free_free_packets (RouteBuffer *o) +{ + while (!LinkedList1_IsEmpty(&o->packets_free)) { + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetLast(&o->packets_free), struct RouteBuffer_packet, node); + + // remove from free packets list + LinkedList1_Remove(&o->packets_free, &p->node); + + // free memory + free(p); + } +} + +static void release_used_packet (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetFirst(&o->packets_used), struct RouteBuffer_packet, node); + + // remove from used packets list + LinkedList1_Remove(&o->packets_used, &p->node); + + // add to free packets list + LinkedList1_Append(&o->packets_free, &p->node); +} + +static void send_used_packet (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetFirst(&o->packets_used), struct RouteBuffer_packet, node); + + // send + PacketPassInterface_Sender_Send(o->output, (uint8_t *)(p + 1), p->len); +} + +static void output_handler_done (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + DebugObject_Access(&o->d_obj); + + // release packet + release_used_packet(o); + + // send next packet if there is one + if (!LinkedList1_IsEmpty(&o->packets_used)) { + send_used_packet(o); + } +} + +int RouteBuffer_Init (RouteBuffer *o, int mtu, PacketPassInterface *output, int buf_size) +{ + ASSERT(mtu >= 0) + ASSERT(PacketPassInterface_GetMTU(output) >= mtu) + ASSERT(buf_size > 0) + + // init arguments + o->mtu = mtu; + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init free packets list + LinkedList1_Init(&o->packets_free); + + // init used packets list + LinkedList1_Init(&o->packets_used); + + // allocate packets + for (int i = 0; i < buf_size; i++) { + if (!alloc_free_packet(o)) { + goto fail1; + } + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + free_free_packets(o); + return 0; +} + +void RouteBuffer_Free (RouteBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // release packets so they can be freed + while (!LinkedList1_IsEmpty(&o->packets_used)) { + release_used_packet(o); + } + + // free packets + free_free_packets(o); +} + +int RouteBuffer_GetMTU (RouteBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + return o->mtu; +} + +int RouteBufferSource_Init (RouteBufferSource *o, int mtu) +{ + ASSERT(mtu >= 0) + + // init arguments + o->mtu = mtu; + + // allocate current packet + if (!(o->current_packet = alloc_packet(o->mtu))) { + goto fail0; + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void RouteBufferSource_Free (RouteBufferSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free current packet + free(o->current_packet); +} + +uint8_t * RouteBufferSource_Pointer (RouteBufferSource *o) +{ + DebugObject_Access(&o->d_obj); + + return (uint8_t *)(o->current_packet + 1); +} + +int RouteBufferSource_Route (RouteBufferSource *o, int len, RouteBuffer *b, int copy_offset, int copy_len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->mtu) + ASSERT(b->mtu == o->mtu) + ASSERT(copy_offset >= 0) + ASSERT(copy_offset <= o->mtu) + ASSERT(copy_len >= 0) + ASSERT(copy_len <= o->mtu - copy_offset) + DebugObject_Access(&b->d_obj); + DebugObject_Access(&o->d_obj); + + // check if there's space in the buffer + if (LinkedList1_IsEmpty(&b->packets_free)) { + return 0; + } + + int was_empty = LinkedList1_IsEmpty(&b->packets_used); + + struct RouteBuffer_packet *p = o->current_packet; + + // set packet length + p->len = len; + + // append packet to used packets list + LinkedList1_Append(&b->packets_used, &p->node); + + // get a free packet + struct RouteBuffer_packet *np = UPPER_OBJECT(LinkedList1_GetLast(&b->packets_free), struct RouteBuffer_packet, node); + + // remove it from free packets list + LinkedList1_Remove(&b->packets_free, &np->node); + + // make it the current packet + o->current_packet = np; + + // copy packet + if (copy_len > 0) { + memcpy((uint8_t *)(np + 1) + copy_offset, (uint8_t *)(p + 1) + copy_offset, copy_len); + } + + // start sending if required + if (was_empty) { + send_used_packet(b); + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/flow/RouteBuffer.h b/app/src/main/jni/badvpn/flow/RouteBuffer.h new file mode 100644 index 0000000..d0f7b41 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/RouteBuffer.h @@ -0,0 +1,139 @@ +/** + * @file RouteBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer for zero-copy packet routing. + */ + +#ifndef BADVPN_FLOW_ROUTEBUFFER_H +#define BADVPN_FLOW_ROUTEBUFFER_H + +#include +#include +#include +#include + +struct RouteBuffer_packet { + LinkedList1Node node; + int len; +}; + +/** + * Packet buffer for zero-copy packet routing. + * + * Packets are buffered using {@link RouteBufferSource} objects. + */ +typedef struct { + int mtu; + PacketPassInterface *output; + LinkedList1 packets_free; + LinkedList1 packets_used; + DebugObject d_obj; +} RouteBuffer; + +/** + * Object through which packets are buffered into {@link RouteBuffer} objects. + * + * A packet is routed by calling {@link RouteBufferSource_Pointer}, writing it to + * the returned address, then calling {@link RouteBufferSource_Route}. + */ +typedef struct { + int mtu; + struct RouteBuffer_packet *current_packet; + DebugObject d_obj; +} RouteBufferSource; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. It will only be possible to route packets to this buffer + * from {@link RouteBufferSource}.s with the same MTU. + * @param output output interface. Its MTU must be >=mtu. + * @param buf_size size of the buffer in number of packet. Must be >0. + * @return 1 on success, 0 on failure + */ +int RouteBuffer_Init (RouteBuffer *o, int mtu, PacketPassInterface *output, int buf_size) WARN_UNUSED; + +/** + * Frees the object. + */ +void RouteBuffer_Free (RouteBuffer *o); + +/** + * Retuns the buffer's MTU (mtu argument to {@link RouteBuffer_Init}). + * + * @return MTU + */ +int RouteBuffer_GetMTU (RouteBuffer *o); + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. The object will only be able to route packets + * to {@link RouteBuffer}'s with the same MTU. + * @return 1 on success, 0 on failure + */ +int RouteBufferSource_Init (RouteBufferSource *o, int mtu) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void RouteBufferSource_Free (RouteBufferSource *o); + +/** + * Returns a pointer to the current packet. + * The pointed to memory area will have space for MTU bytes. + * The pointer is only valid until {@link RouteBufferSource_Route} succeeds. + * + * @param o the object + * @return pointer to the current packet + */ +uint8_t * RouteBufferSource_Pointer (RouteBufferSource *o); + +/** + * Routes the current packet to a given buffer. + * On success, this invalidates the pointer previously returned from + * {@link RouteBufferSource_Pointer}. + * + * @param o the object + * @param len length of the packet. Must be >=0 and <=MTU. + * @param b buffer to route to. Its MTU must equal this object's MTU. + * @param copy_offset Offset from the beginning for copying. Must be >=0 and + * <=mtu. + * @param copy_len Number of bytes to copy from the old current packet to the new one. + * Must be >=0 and <= mtu - copy_offset. + * @return 1 on success, 0 on failure + */ +int RouteBufferSource_Route (RouteBufferSource *o, int len, RouteBuffer *b, int copy_offset, int copy_len); + +#endif diff --git a/app/src/main/jni/badvpn/flow/SinglePacketBuffer.c b/app/src/main/jni/badvpn/flow/SinglePacketBuffer.c new file mode 100644 index 0000000..bbc72ae --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SinglePacketBuffer.c @@ -0,0 +1,87 @@ +/** + * @file SinglePacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (SinglePacketBuffer *o, int in_len) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Sender_Send(o->output, o->buf, in_len); +} + +static void output_handler_done (SinglePacketBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + PacketRecvInterface_Receiver_Recv(o->input, o->buf); +} + +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + + // init arguments + o->input = input; + o->output = output; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init buffer + if (!(o->buf = (uint8_t *)BAlloc(PacketRecvInterface_GetMTU(o->input)))) { + goto fail1; + } + + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, o->buf); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + return 0; +} + +void SinglePacketBuffer_Free (SinglePacketBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + BFree(o->buf); +} diff --git a/app/src/main/jni/badvpn/flow/SinglePacketBuffer.h b/app/src/main/jni/badvpn/flow/SinglePacketBuffer.h new file mode 100644 index 0000000..87314a5 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SinglePacketBuffer.h @@ -0,0 +1,75 @@ +/** + * @file SinglePacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ + +#ifndef BADVPN_FLOW_SINGLEPACKETBUFFER_H +#define BADVPN_FLOW_SINGLEPACKETBUFFER_H + +#include + +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + PacketPassInterface *output; + uint8_t *buf; +} SinglePacketBuffer; + +/** + * Initializes the object. + * Output MTU must be >= input MTU. + * + * @param o the object + * @param input input interface + * @param output output interface + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object + * + * @param o the object + */ +void SinglePacketBuffer_Free (SinglePacketBuffer *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/SinglePacketSender.c b/app/src/main/jni/badvpn/flow/SinglePacketSender.c new file mode 100644 index 0000000..b1f3ec7 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SinglePacketSender.c @@ -0,0 +1,72 @@ +/** + * @file SinglePacketSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void call_handler (SinglePacketSender *o) +{ + DEBUGERROR(&o->d_err, o->handler(o->user)); +} + +static void output_handler_done (SinglePacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + // notify user + call_handler(o); + return; +} + +void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg) +{ + ASSERT(packet_len >= 0) + ASSERT(packet_len <= PacketPassInterface_GetMTU(output)) + + // init arguments + o->output = output; + o->handler = handler; + o->user = user; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // schedule send + PacketPassInterface_Sender_Send(o->output, packet, packet_len); + + DebugObject_Init(&o->d_obj); + DebugError_Init(&o->d_err, pg); +} + +void SinglePacketSender_Free (SinglePacketSender *o) +{ + DebugError_Free(&o->d_err); + DebugObject_Free(&o->d_obj); +} diff --git a/app/src/main/jni/badvpn/flow/SinglePacketSender.h b/app/src/main/jni/badvpn/flow/SinglePacketSender.h new file mode 100644 index 0000000..c9289d8 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SinglePacketSender.h @@ -0,0 +1,82 @@ +/** + * @file SinglePacketSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} source which sends a single packet. + */ + +#ifndef BADVPN_FLOW_SINGLEPACKETSENDER_H +#define BADVPN_FLOW_SINGLEPACKETSENDER_H + +#include + +#include +#include +#include + +/** + * Handler function called after the packet is sent. + * The object must be freed from within this handler. + * + * @param user as in {@link SinglePacketSender_Init}. + */ +typedef void (*SinglePacketSender_handler) (void *user); + +/** + * A {@link PacketPassInterface} source which sends a single packet. + */ +typedef struct { + PacketPassInterface *output; + SinglePacketSender_handler handler; + void *user; + DebugObject d_obj; + DebugError d_err; +} SinglePacketSender; + +/** + * Initializes the object. + * + * @param o the object + * @param packet packet to be sent. Must be available as long as the object exists. + * @param packet_len length of the packet. Must be >=0 and <=(MTU of output). + * @param output output interface + * @param handler handler to call when the packet is sent + * @param user value to pass to handler + * @param pg pending group + */ +void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SinglePacketSender_Free (SinglePacketSender *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/SingleStreamReceiver.c b/app/src/main/jni/badvpn/flow/SingleStreamReceiver.c new file mode 100644 index 0000000..b463290 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SingleStreamReceiver.c @@ -0,0 +1,82 @@ +/** + * @file SingleStreamReceiver.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "SingleStreamReceiver.h" + +static void input_handler_done (SingleStreamReceiver *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->packet_len - o->pos) + + // update position + o->pos += data_len; + + // if everything was received, notify user + if (o->pos == o->packet_len) { + DEBUGERROR(&o->d_err, o->handler(o->user)); + return; + } + + // receive more + StreamRecvInterface_Receiver_Recv(o->input, o->packet + o->pos, o->packet_len - o->pos); +} + +void SingleStreamReceiver_Init (SingleStreamReceiver *o, uint8_t *packet, int packet_len, StreamRecvInterface *input, BPendingGroup *pg, void *user, SingleStreamReceiver_handler handler) +{ + ASSERT(packet_len > 0) + ASSERT(handler) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + o->input = input; + o->user = user; + o->handler = handler; + + // set position zero + o->pos = 0; + + // init output + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->packet + o->pos, o->packet_len - o->pos); + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); +} + +void SingleStreamReceiver_Free (SingleStreamReceiver *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +} diff --git a/app/src/main/jni/badvpn/flow/SingleStreamReceiver.h b/app/src/main/jni/badvpn/flow/SingleStreamReceiver.h new file mode 100644 index 0000000..c9c6219 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SingleStreamReceiver.h @@ -0,0 +1,53 @@ +/** + * @file SingleStreamReceiver.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLESTREAMRECEIVER_H +#define BADVPN_SINGLESTREAMRECEIVER_H + +#include +#include +#include + +typedef void (*SingleStreamReceiver_handler) (void *user); + +typedef struct { + uint8_t *packet; + int packet_len; + StreamRecvInterface *input; + void *user; + SingleStreamReceiver_handler handler; + int pos; + DebugError d_err; + DebugObject d_obj; +} SingleStreamReceiver; + +void SingleStreamReceiver_Init (SingleStreamReceiver *o, uint8_t *packet, int packet_len, StreamRecvInterface *input, BPendingGroup *pg, void *user, SingleStreamReceiver_handler handler); +void SingleStreamReceiver_Free (SingleStreamReceiver *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/SingleStreamSender.c b/app/src/main/jni/badvpn/flow/SingleStreamSender.c new file mode 100644 index 0000000..eb33306 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SingleStreamSender.c @@ -0,0 +1,82 @@ +/** + * @file SingleStreamSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "SingleStreamSender.h" + +static void output_handler_done (SingleStreamSender *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->packet_len - o->pos) + + // update position + o->pos += data_len; + + // if everything was sent, notify user + if (o->pos == o->packet_len) { + DEBUGERROR(&o->d_err, o->handler(o->user)); + return; + } + + // send more + StreamPassInterface_Sender_Send(o->output, o->packet + o->pos, o->packet_len - o->pos); +} + +void SingleStreamSender_Init (SingleStreamSender *o, uint8_t *packet, int packet_len, StreamPassInterface *output, BPendingGroup *pg, void *user, SingleStreamSender_handler handler) +{ + ASSERT(packet_len > 0) + ASSERT(handler) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + o->output = output; + o->user = user; + o->handler = handler; + + // set position zero + o->pos = 0; + + // init output + StreamPassInterface_Sender_Init(o->output, (StreamPassInterface_handler_done)output_handler_done, o); + + // start sending + StreamPassInterface_Sender_Send(o->output, o->packet + o->pos, o->packet_len - o->pos); + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); +} + +void SingleStreamSender_Free (SingleStreamSender *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +} diff --git a/app/src/main/jni/badvpn/flow/SingleStreamSender.h b/app/src/main/jni/badvpn/flow/SingleStreamSender.h new file mode 100644 index 0000000..180a9bf --- /dev/null +++ b/app/src/main/jni/badvpn/flow/SingleStreamSender.h @@ -0,0 +1,53 @@ +/** + * @file SingleStreamSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLESTREAMSENDER_H +#define BADVPN_SINGLESTREAMSENDER_H + +#include +#include +#include + +typedef void (*SingleStreamSender_handler) (void *user); + +typedef struct { + uint8_t *packet; + int packet_len; + StreamPassInterface *output; + void *user; + SingleStreamSender_handler handler; + int pos; + DebugError d_err; + DebugObject d_obj; +} SingleStreamSender; + +void SingleStreamSender_Init (SingleStreamSender *o, uint8_t *packet, int packet_len, StreamPassInterface *output, BPendingGroup *pg, void *user, SingleStreamSender_handler handler); +void SingleStreamSender_Free (SingleStreamSender *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/StreamPacketSender.c b/app/src/main/jni/badvpn/flow/StreamPacketSender.c new file mode 100644 index 0000000..1e0a949 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPacketSender.c @@ -0,0 +1,90 @@ +/** + * @file StreamPacketSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "StreamPacketSender.h" + +static void input_handler_send (StreamPacketSender *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + + // limit length to MTU and remember + if (data_len > o->output_mtu) { + o->sending_len = o->output_mtu; + } else { + o->sending_len = data_len; + } + + // send + PacketPassInterface_Sender_Send(o->output, data, o->sending_len); +} + +static void output_handler_done (StreamPacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + // done + StreamPassInterface_Done(&o->input, o->sending_len); +} + +void StreamPacketSender_Init (StreamPacketSender *o, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) > 0) + + // init arguments + o->output = output; + + // remember output MTU + o->output_mtu = PacketPassInterface_GetMTU(output); + + // init input + StreamPassInterface_Init(&o->input, (StreamPassInterface_handler_send)input_handler_send, o, pg); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + DebugObject_Init(&o->d_obj); +} + +void StreamPacketSender_Free (StreamPacketSender *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + StreamPassInterface_Free(&o->input); +} + +StreamPassInterface * StreamPacketSender_GetInput (StreamPacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/app/src/main/jni/badvpn/flow/StreamPacketSender.h b/app/src/main/jni/badvpn/flow/StreamPacketSender.h new file mode 100644 index 0000000..19bda5e --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPacketSender.h @@ -0,0 +1,77 @@ +/** + * @file StreamPacketSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STREAMPACKETSENDER_H +#define BADVPN_STREAMPACKETSENDER_H + +#include +#include +#include + +/** + * Object which breaks an input stream into output packets. The resulting + * packets will have positive length, and, when concatenated, will form the + * original stream. + * + * Input is with {@link StreamPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + PacketPassInterface *output; + int output_mtu; + StreamPassInterface input; + int sending_len; + DebugObject d_obj; +} StreamPacketSender; + +/** + * Initializes the object. + * + * @param o the object + * @param output output interface. Its MTU must be >0. + * @param pg pending group we live in + */ +void StreamPacketSender_Init (StreamPacketSender *o, PacketPassInterface *output, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamPacketSender_Free (StreamPacketSender *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +StreamPassInterface * StreamPacketSender_GetInput (StreamPacketSender *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/StreamPassConnector.c b/app/src/main/jni/badvpn/flow/StreamPassConnector.c new file mode 100644 index 0000000..d3075c7 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPassConnector.c @@ -0,0 +1,120 @@ +/** + * @file StreamPassConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (StreamPassConnector *o, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + o->in = data; + + if (o->output) { + // schedule send + StreamPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +static void output_handler_done (StreamPassConnector *o, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= o->in_len) + ASSERT(o->in_len > 0) + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // have no input packet + o->in_len = -1; + + // allow input to send more packets + StreamPassInterface_Done(&o->input, data_len); +} + +void StreamPassConnector_Init (StreamPassConnector *o, BPendingGroup *pg) +{ + // init output + StreamPassInterface_Init(&o->input, (StreamPassInterface_handler_send)input_handler_send, o, pg); + + // have no input packet + o->in_len = -1; + + // have no output + o->output = NULL; + + DebugObject_Init(&o->d_obj); +} + +void StreamPassConnector_Free (StreamPassConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + StreamPassInterface_Free(&o->input); +} + +StreamPassInterface * StreamPassConnector_GetInput (StreamPassConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void StreamPassConnector_ConnectOutput (StreamPassConnector *o, StreamPassInterface *output) +{ + ASSERT(!o->output) + DebugObject_Access(&o->d_obj); + + // set output + o->output = output; + + // init output + StreamPassInterface_Sender_Init(o->output, (StreamPassInterface_handler_done)output_handler_done, o); + + // if we have an input packet, schedule send + if (o->in_len > 0) { + StreamPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +void StreamPassConnector_DisconnectOutput (StreamPassConnector *o) +{ + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // set no output + o->output = NULL; +} diff --git a/app/src/main/jni/badvpn/flow/StreamPassConnector.h b/app/src/main/jni/badvpn/flow/StreamPassConnector.h new file mode 100644 index 0000000..aa79195 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPassConnector.h @@ -0,0 +1,98 @@ +/** + * @file StreamPassConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link StreamPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_STREAMPASSCONNECTOR_H +#define BADVPN_FLOW_STREAMPASSCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link StreamPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ +typedef struct { + StreamPassInterface input; + int in_len; + uint8_t *in; + StreamPassInterface *output; + DebugObject d_obj; +} StreamPassConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param pg pending group + */ +void StreamPassConnector_Init (StreamPassConnector *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamPassConnector_Free (StreamPassConnector *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +StreamPassInterface * StreamPassConnector_GetInput (StreamPassConnector *o); + +/** + * Connects output. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output output to connect + */ +void StreamPassConnector_ConnectOutput (StreamPassConnector *o, StreamPassInterface *output); + +/** + * Disconnects output. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void StreamPassConnector_DisconnectOutput (StreamPassConnector *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/StreamPassInterface.c b/app/src/main/jni/badvpn/flow/StreamPassInterface.c new file mode 100644 index 0000000..f0dc547 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPassInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamPassInterface_job_operation (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamPassInterface_job_done (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/app/src/main/jni/badvpn/flow/StreamPassInterface.h b/app/src/main/jni/badvpn/flow/StreamPassInterface.h new file mode 100644 index 0000000..1fe650e --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamPassInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream sender to pass stream data to a stream receiver. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamRecvInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamRecvInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMPASSINTERFACE_H +#define BADVPN_FLOW_STREAMPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SPI_STATE_NONE 1 +#define SPI_STATE_OPERATION_PENDING 2 +#define SPI_STATE_BUSY 3 +#define SPI_STATE_DONE_PENDING 4 + +typedef void (*StreamPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamPassInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamPassInterface_handler_send handler_operation; + void *user_provider; + + // user data + StreamPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamPassInterface; + +static void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void StreamPassInterface_Free (StreamPassInterface *i); + +static void StreamPassInterface_Done (StreamPassInterface *i, int data_len); + +static void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user); + +static void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len); + +void _StreamPassInterface_job_operation (StreamPassInterface *i); +void _StreamPassInterface_job_done (StreamPassInterface *i); + +void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamPassInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamPassInterface_job_done, i); + + // set state + i->state = SPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamPassInterface_Free (StreamPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamPassInterface_Done (StreamPassInterface *i, int data_len) +{ + ASSERT(i->state == SPI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SPI_STATE_DONE_PENDING; +} + +void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SPI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/app/src/main/jni/badvpn/flow/StreamRecvConnector.c b/app/src/main/jni/badvpn/flow/StreamRecvConnector.c new file mode 100644 index 0000000..beb6a88 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamRecvConnector.c @@ -0,0 +1,120 @@ +/** + * @file StreamRecvConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void output_handler_recv (StreamRecvConnector *o, uint8_t *data, int data_avail) +{ + ASSERT(data_avail > 0) + ASSERT(o->out_avail == -1) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_avail = data_avail; + o->out = data; + + if (o->input) { + // schedule receive + StreamRecvInterface_Receiver_Recv(o->input, o->out, o->out_avail); + } +} + +static void input_handler_done (StreamRecvConnector *o, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= o->out_avail) + ASSERT(o->out_avail > 0) + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // have no output packet + o->out_avail = -1; + + // allow output to receive more packets + StreamRecvInterface_Done(&o->output, data_len); +} + +void StreamRecvConnector_Init (StreamRecvConnector *o, BPendingGroup *pg) +{ + // init output + StreamRecvInterface_Init(&o->output, (StreamRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_avail = -1; + + // have no input + o->input = NULL; + + DebugObject_Init(&o->d_obj); +} + +void StreamRecvConnector_Free (StreamRecvConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + StreamRecvInterface_Free(&o->output); +} + +StreamRecvInterface * StreamRecvConnector_GetOutput (StreamRecvConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void StreamRecvConnector_ConnectInput (StreamRecvConnector *o, StreamRecvInterface *input) +{ + ASSERT(!o->input) + DebugObject_Access(&o->d_obj); + + // set input + o->input = input; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // if we have an output packet, schedule receive + if (o->out_avail > 0) { + StreamRecvInterface_Receiver_Recv(o->input, o->out, o->out_avail); + } +} + +void StreamRecvConnector_DisconnectInput (StreamRecvConnector *o) +{ + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // set no input + o->input = NULL; +} diff --git a/app/src/main/jni/badvpn/flow/StreamRecvConnector.h b/app/src/main/jni/badvpn/flow/StreamRecvConnector.h new file mode 100644 index 0000000..ed111ce --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamRecvConnector.h @@ -0,0 +1,98 @@ +/** + * @file StreamRecvConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link StreamRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_STREAMRECVCONNECTOR_H +#define BADVPN_FLOW_STREAMRECVCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link StreamRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ +typedef struct { + StreamRecvInterface output; + int out_avail; + uint8_t *out; + StreamRecvInterface *input; + DebugObject d_obj; +} StreamRecvConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param pg pending group + */ +void StreamRecvConnector_Init (StreamRecvConnector *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamRecvConnector_Free (StreamRecvConnector *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +StreamRecvInterface * StreamRecvConnector_GetOutput (StreamRecvConnector *o); + +/** + * Connects input. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output input to connect + */ +void StreamRecvConnector_ConnectInput (StreamRecvConnector *o, StreamRecvInterface *input); + +/** + * Disconnects input. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void StreamRecvConnector_DisconnectInput (StreamRecvConnector *o); + +#endif diff --git a/app/src/main/jni/badvpn/flow/StreamRecvInterface.c b/app/src/main/jni/badvpn/flow/StreamRecvInterface.c new file mode 100644 index 0000000..697e1ae --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamRecvInterface_job_done (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/app/src/main/jni/badvpn/flow/StreamRecvInterface.h b/app/src/main/jni/badvpn/flow/StreamRecvInterface.h new file mode 100644 index 0000000..cd4a181 --- /dev/null +++ b/app/src/main/jni/badvpn/flow/StreamRecvInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream receiver to receive stream data from a stream sender. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamPassInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamPassInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMRECVINTERFACE_H +#define BADVPN_FLOW_STREAMRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SRI_STATE_NONE 1 +#define SRI_STATE_OPERATION_PENDING 2 +#define SRI_STATE_BUSY 3 +#define SRI_STATE_DONE_PENDING 4 + +typedef void (*StreamRecvInterface_handler_recv) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + StreamRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamRecvInterface; + +static void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void StreamRecvInterface_Free (StreamRecvInterface *i); + +static void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len); + +static void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user); + +static void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len); + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i); +void _StreamRecvInterface_job_done (StreamRecvInterface *i); + +void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamRecvInterface_job_done, i); + + // set state + i->state = SRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamRecvInterface_Free (StreamRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len) +{ + ASSERT(i->state == SRI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SRI_STATE_DONE_PENDING; +} + +void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/app/src/main/jni/badvpn/flowextra/CMakeLists.txt b/app/src/main/jni/badvpn/flowextra/CMakeLists.txt new file mode 100644 index 0000000..cf89e3c --- /dev/null +++ b/app/src/main/jni/badvpn/flowextra/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(flowextra + PacketPassInactivityMonitor.c + KeepaliveIO.c +) +target_link_libraries(flowextra flow system) diff --git a/app/src/main/jni/badvpn/flowextra/KeepaliveIO.c b/app/src/main/jni/badvpn/flowextra/KeepaliveIO.c new file mode 100644 index 0000000..4e80366 --- /dev/null +++ b/app/src/main/jni/badvpn/flowextra/KeepaliveIO.c @@ -0,0 +1,112 @@ +/** + * @file KeepaliveIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "KeepaliveIO.h" + +static void keepalive_handler (KeepaliveIO *o) +{ + DebugObject_Access(&o->d_obj); + + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); +} + +int KeepaliveIO_Init (KeepaliveIO *o, BReactor *reactor, PacketPassInterface *output, PacketRecvInterface *keepalive_input, btime_t keepalive_interval_ms) +{ + ASSERT(PacketRecvInterface_GetMTU(keepalive_input) <= PacketPassInterface_GetMTU(output)) + ASSERT(keepalive_interval_ms > 0) + + // set arguments + o->reactor = reactor; + + // init keep-alive sender + PacketPassInactivityMonitor_Init(&o->kasender, output, o->reactor, keepalive_interval_ms, (PacketPassInactivityMonitor_handler)keepalive_handler, o); + + // init queue + PacketPassPriorityQueue_Init(&o->queue, PacketPassInactivityMonitor_GetInput(&o->kasender), BReactor_PendingGroup(o->reactor), 0); + + // init keepalive flow + PacketPassPriorityQueueFlow_Init(&o->ka_qflow, &o->queue, -1); + + // init keepalive blocker + PacketRecvBlocker_Init(&o->ka_blocker, keepalive_input, BReactor_PendingGroup(reactor)); + + // init keepalive buffer + if (!SinglePacketBuffer_Init(&o->ka_buffer, PacketRecvBlocker_GetOutput(&o->ka_blocker), PacketPassPriorityQueueFlow_GetInput(&o->ka_qflow), BReactor_PendingGroup(o->reactor))) { + goto fail1; + } + + // init user flow + PacketPassPriorityQueueFlow_Init(&o->user_qflow, &o->queue, 0); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketRecvBlocker_Free(&o->ka_blocker); + PacketPassPriorityQueueFlow_Free(&o->ka_qflow); + PacketPassPriorityQueue_Free(&o->queue); + PacketPassInactivityMonitor_Free(&o->kasender); + return 0; +} + +void KeepaliveIO_Free (KeepaliveIO *o) +{ + DebugObject_Free(&o->d_obj); + + // allow freeing queue flows + PacketPassPriorityQueue_PrepareFree(&o->queue); + + // free user flow + PacketPassPriorityQueueFlow_Free(&o->user_qflow); + + // free keepalive buffer + SinglePacketBuffer_Free(&o->ka_buffer); + + // free keepalive blocker + PacketRecvBlocker_Free(&o->ka_blocker); + + // free keepalive flow + PacketPassPriorityQueueFlow_Free(&o->ka_qflow); + + // free queue + PacketPassPriorityQueue_Free(&o->queue); + + // free keep-alive sender + PacketPassInactivityMonitor_Free(&o->kasender); +} + +PacketPassInterface * KeepaliveIO_GetInput (KeepaliveIO *o) +{ + DebugObject_Access(&o->d_obj); + + return PacketPassPriorityQueueFlow_GetInput(&o->user_qflow); +} diff --git a/app/src/main/jni/badvpn/flowextra/KeepaliveIO.h b/app/src/main/jni/badvpn/flowextra/KeepaliveIO.h new file mode 100644 index 0000000..d89321c --- /dev/null +++ b/app/src/main/jni/badvpn/flowextra/KeepaliveIO.h @@ -0,0 +1,88 @@ +/** + * @file KeepaliveIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer for sending keep-alive packets. + */ + +#ifndef BADVPN_KEEPALIVEIO +#define BADVPN_KEEPALIVEIO + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * A {@link PacketPassInterface} layer for sending keep-alive packets. + */ +typedef struct { + BReactor *reactor; + PacketPassInactivityMonitor kasender; + PacketPassPriorityQueue queue; + PacketPassPriorityQueueFlow user_qflow; + PacketPassPriorityQueueFlow ka_qflow; + SinglePacketBuffer ka_buffer; + PacketRecvBlocker ka_blocker; + DebugObject d_obj; +} KeepaliveIO; + +/** + * Initializes the object. + * + * @param o the object + * @param reactor reactor we live in + * @param output output interface + * @param keepalive_input keepalive input interface. Its MTU must be <= MTU of output. + * @param keepalive_interval_ms keepalive interval in milliseconds. Must be >0. + * @return 1 on success, 0 on failure + */ +int KeepaliveIO_Init (KeepaliveIO *o, BReactor *reactor, PacketPassInterface *output, PacketRecvInterface *keepalive_input, btime_t keepalive_interval_ms) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void KeepaliveIO_Free (KeepaliveIO *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * KeepaliveIO_GetInput (KeepaliveIO *o); + +#endif diff --git a/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c b/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c new file mode 100644 index 0000000..6531fe0 --- /dev/null +++ b/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c @@ -0,0 +1,131 @@ +/** + * @file PacketPassInactivityMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "PacketPassInactivityMonitor.h" + +static void input_handler_send (PacketPassInactivityMonitor *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // schedule send + PacketPassInterface_Sender_Send(o->output, data, data_len); + + // stop timer + BReactor_RemoveTimer(o->reactor, &o->timer); +} + +static void input_handler_requestcancel (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(o->output); +} + +static void output_handler_done (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // output no longer busy, restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call done + PacketPassInterface_Done(&o->input); +} + +static void timer_handler (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call handler + if (o->handler) { + o->handler(o->user); + return; + } +} + +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user) +{ + // init arguments + o->output = output; + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor)); + if (PacketPassInterface_HasCancel(o->output)) { + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + } + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init timer + BTimer_Init(&o->timer, interval, (BTimer_handler)timer_handler, o); + BReactor_SetTimer(o->reactor, &o->timer); + + DebugObject_Init(&o->d_obj); +} + +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o) +{ + DebugObject_Free(&o->d_obj); + + // free timer + BReactor_RemoveTimer(o->reactor, &o->timer); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->user = user; +} + +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + BReactor_SetTimerAfter(o->reactor, &o->timer, 0); +} diff --git a/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h b/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h new file mode 100644 index 0000000..5857618 --- /dev/null +++ b/app/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h @@ -0,0 +1,124 @@ +/** + * @file PacketPassInactivityMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer for detecting inactivity. + */ + +#ifndef BADVPN_PACKETPASSINACTIVITYMONITOR_H +#define BADVPN_PACKETPASSINACTIVITYMONITOR_H + +#include +#include +#include + +/** + * Handler function invoked when inactivity is detected. + * It is guaranteed that the interfaces are in not sending state. + * + * @param user value given to {@link PacketPassInactivityMonitor_Init} + */ +typedef void (*PacketPassInactivityMonitor_handler) (void *user); + +/** + * A {@link PacketPassInterface} layer for detecting inactivity. + * It reports inactivity to a user provided handler function. + * + * The object behaves like that: + * ("timer set" means started with the given timeout whether if was running or not, + * "timer unset" means stopped if it was running) + * - There is a timer. + * - The timer is set when the object is initialized. + * - When the input calls Send, the call is passed on to the output. + * If the output accepted the packet, the timer is set. If the output + * blocked the packet, the timer is unset. + * - When the output calls Done, the timer is set, and the call is + * passed on to the input. + * - When the input calls Cancel, the timer is set, and the call is + * passed on to the output. + * - When the timer expires, the timer is set, ant the user's handler + * function is invoked. + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface *output; + BReactor *reactor; + PacketPassInactivityMonitor_handler handler; + void *user; + PacketPassInterface input; + BTimer timer; +} PacketPassInactivityMonitor; + +/** + * Initializes the object. + * See {@link PacketPassInactivityMonitor} for details. + * + * @param o the object + * @param output output interface + * @param reactor reactor we live in + * @param interval timer value in milliseconds + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o); + +/** + * Returns the input interface. + * The MTU of the interface will be the same as of the output interface. + * The interface supports cancel functionality if the output interface supports it. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o); + +/** + * Sets or removes the inactivity handler. + * + * @param o the object + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Sets the timer to expire immediately in order to force an inactivity report. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o); + +#endif diff --git a/app/src/main/jni/badvpn/generate_files b/app/src/main/jni/badvpn/generate_files new file mode 100644 index 0000000..f473369 --- /dev/null +++ b/app/src/main/jni/badvpn/generate_files @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +PHP_CMD=( php ) +FLEX_CMD=( flex ) +BISON_CMD=( bison ) + +OUT_DIR="generated/" + +function bproto() { + local input="$1" + local name="$2" + "${PHP_CMD[@]}" bproto_generator/bproto.php --input-file "${input}" --output-dir "${OUT_DIR}" --name "bproto_${name}" +} + +function do_flex() { + local input="$1" + local name="$2" + "${FLEX_CMD[@]}" -o "${OUT_DIR}/flex_${name}.c" --header-file="${OUT_DIR}/flex_${name}.h" "${input}" + "${PHP_CMD[@]}" fix_flex.php "${OUT_DIR}/flex_${name}.c" + "${PHP_CMD[@]}" fix_flex.php "${OUT_DIR}/flex_${name}.h" +} + +function do_bison() { + local input="$1" + local name="$2" + "${BISON_CMD[@]}" -d -o "${OUT_DIR}/bison_${name}.c" "${input}" +} + +function do_lemon() { + local input="$1" + local name=$(basename "${input}") + ( + cd generated && + rm -f "${name}" && + cp ../"${input}" "${name}" && + ../lemon/lemon "${name}" + ) +} + +mkdir -p generated + +bproto tests/bproto_test.bproto bproto_test +bproto protocol/msgproto.bproto msgproto +bproto protocol/addr.bproto addr +do_flex predicate/BPredicate.l BPredicate +do_bison predicate/BPredicate.y BPredicate +"${PHP_CMD[@]}" blog_generator/blog.php --input-file blog_channels.txt --output-dir "${OUT_DIR}" +do_lemon ncd/NCDConfigParser_parse.y +do_lemon ncd/NCDValParser_parse.y diff --git a/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.c b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.c new file mode 100644 index 0000000..3ebdceb --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.c @@ -0,0 +1,1890 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +#line 30 "NCDConfigParser_parse.y" + + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +#line 62 "NCDConfigParser_parse.c" +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +#define YYCODETYPE unsigned char +#define YYNOCODE 41 +#define YYACTIONTYPE unsigned char +#define ParseTOKENTYPE struct token +typedef union { + int yyinit; + ParseTOKENTYPE yy0; + int yy12; + char * yy33; + struct block yy37; + struct value yy51; + struct program yy54; + struct ifblock yy76; + struct statement yy79; +} YYMINORTYPE; +#ifndef YYSTACKDEPTH +#define YYSTACKDEPTH 0 +#endif +#define ParseARG_SDECL struct parser_out *parser_out ; +#define ParseARG_PDECL , struct parser_out *parser_out +#define ParseARG_FETCH struct parser_out *parser_out = yypParser->parser_out +#define ParseARG_STORE yypParser->parser_out = parser_out +#define YYNSTATE 99 +#define YYNRULE 38 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static const YYACTIONTYPE yy_action[] = { + /* 0 */ 86, 39, 80, 87, 68, 88, 42, 86, 48, 80, + /* 10 */ 87, 1, 88, 42, 24, 85, 78, 41, 3, 82, + /* 20 */ 86, 40, 43, 87, 41, 88, 42, 41, 85, 79, + /* 30 */ 41, 3, 4, 86, 50, 56, 87, 46, 88, 44, + /* 40 */ 47, 85, 49, 41, 3, 4, 89, 34, 86, 35, + /* 50 */ 81, 87, 72, 88, 42, 73, 54, 86, 4, 55, + /* 60 */ 87, 84, 88, 44, 26, 97, 36, 75, 76, 36, + /* 70 */ 86, 61, 66, 87, 86, 88, 45, 87, 86, 88, + /* 80 */ 51, 87, 86, 88, 57, 87, 33, 88, 69, 15, + /* 90 */ 59, 27, 98, 38, 31, 99, 62, 37, 18, 15, + /* 100 */ 36, 138, 15, 53, 31, 15, 67, 31, 15, 60, + /* 110 */ 31, 15, 94, 31, 15, 65, 31, 19, 71, 31, + /* 120 */ 20, 11, 77, 23, 74, 22, 5, 83, 6, 90, + /* 130 */ 2, 91, 7, 25, 8, 12, 52, 21, 36, 13, + /* 140 */ 9, 92, 58, 32, 28, 14, 93, 63, 29, 64, + /* 150 */ 16, 139, 96, 95, 10, 17, 70, 30, +}; +static const YYCODETYPE yy_lookahead[] = { + /* 0 */ 30, 31, 32, 33, 15, 35, 36, 30, 31, 32, + /* 10 */ 33, 7, 35, 36, 10, 2, 4, 4, 5, 6, + /* 20 */ 30, 37, 32, 33, 4, 35, 36, 4, 2, 30, + /* 30 */ 4, 5, 19, 30, 11, 12, 33, 34, 35, 36, + /* 40 */ 30, 2, 37, 4, 5, 19, 20, 1, 30, 3, + /* 50 */ 32, 33, 24, 35, 36, 24, 37, 30, 19, 16, + /* 60 */ 33, 34, 35, 36, 26, 27, 38, 21, 22, 38, + /* 70 */ 30, 37, 37, 33, 30, 35, 36, 33, 30, 35, + /* 80 */ 36, 33, 30, 35, 36, 33, 24, 35, 36, 25, + /* 90 */ 8, 28, 27, 29, 30, 0, 14, 4, 2, 25, + /* 100 */ 38, 39, 25, 29, 30, 25, 29, 30, 25, 29, + /* 110 */ 30, 25, 29, 30, 25, 29, 30, 2, 29, 30, + /* 120 */ 6, 5, 9, 17, 24, 8, 18, 6, 18, 20, + /* 130 */ 7, 9, 14, 8, 7, 5, 8, 6, 38, 5, + /* 140 */ 7, 9, 13, 4, 6, 5, 9, 4, 6, 8, + /* 150 */ 5, 40, 6, 9, 7, 5, 8, 6, +}; +#define YY_SHIFT_USE_DFLT (-12) +#define YY_SHIFT_MAX 71 +static const short yy_shift_ofst[] = { + /* 0 */ 46, 39, 39, 13, 26, 39, 39, 39, 39, 39, + /* 10 */ 39, 23, 23, 23, 23, 23, 23, 23, 46, 46, + /* 20 */ 46, -11, 12, 20, 20, 12, 43, 12, 12, 12, + /* 30 */ -11, 4, 82, 95, 96, 115, 93, 116, 114, 117, + /* 40 */ 113, 106, 108, 121, 118, 110, 109, 123, 125, 122, + /* 50 */ 127, 128, 130, 131, 132, 134, 133, 129, 139, 140, + /* 60 */ 138, 137, 143, 141, 145, 142, 144, 146, 147, 148, + /* 70 */ 150, 151, +}; +#define YY_REDUCE_USE_DFLT (-31) +#define YY_REDUCE_MAX 30 +static const signed char yy_reduce_ofst[] = { + /* 0 */ 62, -30, -23, -10, 3, 18, 27, 40, 44, 48, + /* 10 */ 52, 64, 74, 77, 80, 83, 86, 89, 28, 31, + /* 20 */ 100, 38, -16, -1, 10, 5, 63, 19, 34, 35, + /* 30 */ 65, +}; +static const YYACTIONTYPE yy_default[] = { + /* 0 */ 100, 119, 119, 137, 137, 137, 137, 137, 137, 137, + /* 10 */ 137, 137, 137, 137, 137, 115, 137, 137, 100, 100, + /* 20 */ 100, 109, 133, 137, 137, 133, 113, 133, 133, 133, + /* 30 */ 111, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 40 */ 137, 117, 121, 137, 137, 125, 137, 137, 137, 137, + /* 50 */ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 60 */ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 70 */ 137, 137, 101, 102, 103, 135, 136, 104, 134, 118, + /* 80 */ 120, 122, 123, 124, 126, 129, 130, 131, 132, 127, + /* 90 */ 128, 105, 106, 107, 116, 108, 114, 110, 112, +}; +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { + "$", "INCLUDE", "STRING", "INCLUDE_GUARD", + "NAME", "CURLY_OPEN", "CURLY_CLOSE", "ROUND_OPEN", + "ROUND_CLOSE", "SEMICOLON", "ARROW", "IF", + "FOREACH", "AS", "COLON", "ELIF", + "ELSE", "DOT", "COMMA", "BRACKET_OPEN", + "BRACKET_CLOSE", "PROCESS", "TEMPLATE", "error", + "processes", "statement", "elif_maybe", "elif", + "else_maybe", "statements", "dotted_name", "statement_args_maybe", + "list_contents", "list", "map_contents", "map", + "value", "name_maybe", "process_or_template", "input", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { + /* 0 */ "input ::= processes", + /* 1 */ "processes ::=", + /* 2 */ "processes ::= INCLUDE STRING processes", + /* 3 */ "processes ::= INCLUDE_GUARD STRING processes", + /* 4 */ "processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes", + /* 5 */ "statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON", + /* 6 */ "statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON", + /* 7 */ "statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON", + /* 8 */ "statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON", + /* 9 */ "statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON", + /* 10 */ "elif_maybe ::=", + /* 11 */ "elif_maybe ::= elif", + /* 12 */ "elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE", + /* 13 */ "elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif", + /* 14 */ "else_maybe ::=", + /* 15 */ "else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE", + /* 16 */ "statements ::= statement", + /* 17 */ "statements ::= statement statements", + /* 18 */ "dotted_name ::= NAME", + /* 19 */ "dotted_name ::= NAME DOT dotted_name", + /* 20 */ "statement_args_maybe ::=", + /* 21 */ "statement_args_maybe ::= list_contents", + /* 22 */ "list_contents ::= value", + /* 23 */ "list_contents ::= value COMMA list_contents", + /* 24 */ "list ::= CURLY_OPEN CURLY_CLOSE", + /* 25 */ "list ::= CURLY_OPEN list_contents CURLY_CLOSE", + /* 26 */ "map_contents ::= value COLON value", + /* 27 */ "map_contents ::= value COLON value COMMA map_contents", + /* 28 */ "map ::= BRACKET_OPEN BRACKET_CLOSE", + /* 29 */ "map ::= BRACKET_OPEN map_contents BRACKET_CLOSE", + /* 30 */ "value ::= STRING", + /* 31 */ "value ::= dotted_name", + /* 32 */ "value ::= list", + /* 33 */ "value ::= map", + /* 34 */ "name_maybe ::=", + /* 35 */ "name_maybe ::= NAME", + /* 36 */ "process_or_template ::= PROCESS", + /* 37 */ "process_or_template ::= TEMPLATE", +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + /* TERMINAL Destructor */ + case 1: /* INCLUDE */ + case 2: /* STRING */ + case 3: /* INCLUDE_GUARD */ + case 4: /* NAME */ + case 5: /* CURLY_OPEN */ + case 6: /* CURLY_CLOSE */ + case 7: /* ROUND_OPEN */ + case 8: /* ROUND_CLOSE */ + case 9: /* SEMICOLON */ + case 10: /* ARROW */ + case 11: /* IF */ + case 12: /* FOREACH */ + case 13: /* AS */ + case 14: /* COLON */ + case 15: /* ELIF */ + case 16: /* ELSE */ + case 17: /* DOT */ + case 18: /* COMMA */ + case 19: /* BRACKET_OPEN */ + case 20: /* BRACKET_CLOSE */ + case 21: /* PROCESS */ + case 22: /* TEMPLATE */ +{ +#line 89 "NCDConfigParser_parse.y" + free_token((yypminor->yy0)); +#line 523 "NCDConfigParser_parse.c" +} + break; + case 24: /* processes */ +{ +#line 108 "NCDConfigParser_parse.y" + (void)parser_out; free_program((yypminor->yy54)); +#line 530 "NCDConfigParser_parse.c" +} + break; + case 25: /* statement */ +{ +#line 109 "NCDConfigParser_parse.y" + free_statement((yypminor->yy79)); +#line 537 "NCDConfigParser_parse.c" +} + break; + case 26: /* elif_maybe */ + case 27: /* elif */ +{ +#line 110 "NCDConfigParser_parse.y" + free_ifblock((yypminor->yy76)); +#line 545 "NCDConfigParser_parse.c" +} + break; + case 28: /* else_maybe */ + case 29: /* statements */ +{ +#line 112 "NCDConfigParser_parse.y" + free_block((yypminor->yy37)); +#line 553 "NCDConfigParser_parse.c" +} + break; + case 30: /* dotted_name */ + case 37: /* name_maybe */ +{ +#line 114 "NCDConfigParser_parse.y" + free((yypminor->yy33)); +#line 561 "NCDConfigParser_parse.c" +} + break; + case 31: /* statement_args_maybe */ + case 32: /* list_contents */ + case 33: /* list */ + case 34: /* map_contents */ + case 35: /* map */ + case 36: /* value */ +{ +#line 115 "NCDConfigParser_parse.y" + free_value((yypminor->yy51)); +#line 573 "NCDConfigParser_parse.c" +} + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +#line 130 "NCDConfigParser_parse.y" + + if (yypMinor) { + free_token(yypMinor->yy0); + } +#line 751 "NCDConfigParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 39, 1 }, + { 24, 0 }, + { 24, 3 }, + { 24, 3 }, + { 24, 6 }, + { 25, 6 }, + { 25, 8 }, + { 25, 11 }, + { 25, 11 }, + { 25, 13 }, + { 26, 0 }, + { 26, 1 }, + { 27, 7 }, + { 27, 8 }, + { 28, 0 }, + { 28, 4 }, + { 29, 1 }, + { 29, 2 }, + { 30, 1 }, + { 30, 3 }, + { 31, 0 }, + { 31, 1 }, + { 32, 1 }, + { 32, 3 }, + { 33, 2 }, + { 33, 3 }, + { 34, 3 }, + { 34, 5 }, + { 35, 2 }, + { 35, 3 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 37, 0 }, + { 37, 1 }, + { 38, 1 }, + { 38, 1 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ + case 0: /* input ::= processes */ +#line 136 "NCDConfigParser_parse.y" +{ + ASSERT(!parser_out->have_ast) + + if (yymsp[0].minor.yy54.have) { + parser_out->have_ast = 1; + parser_out->ast = yymsp[0].minor.yy54.v; + } +} +#line 910 "NCDConfigParser_parse.c" + break; + case 1: /* processes ::= */ +#line 145 "NCDConfigParser_parse.y" +{ + NCDProgram prog; + NCDProgram_Init(&prog); + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = prog; +} +#line 921 "NCDConfigParser_parse.c" + break; + case 2: /* processes ::= INCLUDE STRING processes */ +#line 153 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-1].minor.yy0.str) + if (!yymsp[0].minor.yy54.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, yymsp[-1].minor.yy0.str, yymsp[-1].minor.yy0.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failA1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(yymsp[-1].minor.yy0); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,1,&yymsp[-2].minor); +} +#line 955 "NCDConfigParser_parse.c" + break; + case 3: /* processes ::= INCLUDE_GUARD STRING processes */ +#line 183 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-1].minor.yy0.str) + if (!yymsp[0].minor.yy54.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, yymsp[-1].minor.yy0.str, yymsp[-1].minor.yy0.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failZ1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(yymsp[-1].minor.yy0); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,3,&yymsp[-2].minor); +} +#line 989 "NCDConfigParser_parse.c" + break; + case 4: /* processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes */ +#line 213 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-4].minor.yy0.str) + if (!yymsp[-2].minor.yy37.have || !yymsp[0].minor.yy54.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, yymsp[-5].minor.yy12, yymsp[-4].minor.yy0.str, yymsp[-2].minor.yy37.v)) { + goto failB0; + } + yymsp[-2].minor.yy37.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failB1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(yymsp[-4].minor.yy0); + free_block(yymsp[-2].minor.yy37); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,5,&yymsp[-3].minor); + yy_destructor(yypParser,6,&yymsp[-1].minor); +} +#line 1029 "NCDConfigParser_parse.c" + break; + case 5: /* statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */ +#line 248 "NCDConfigParser_parse.y" +{ + if (!yymsp[-5].minor.yy33 || !yymsp[-3].minor.yy51.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&yygotominor.yy79.v, yymsp[-1].minor.yy33, NULL, yymsp[-5].minor.yy33, yymsp[-3].minor.yy51.v)) { + goto failC0; + } + yymsp[-3].minor.yy51.have = 0; + + yygotominor.yy79.have = 1; + goto doneC; + +failC0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(yymsp[-5].minor.yy33); + free_value(yymsp[-3].minor.yy51); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,7,&yymsp[-4].minor); + yy_destructor(yypParser,8,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1057 "NCDConfigParser_parse.c" + break; + case 6: /* statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */ +#line 270 "NCDConfigParser_parse.y" +{ + if (!yymsp[-7].minor.yy33 || !yymsp[-5].minor.yy33 || !yymsp[-3].minor.yy51.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-7].minor.yy33, yymsp[-5].minor.yy33, yymsp[-3].minor.yy51.v)) { + goto failD0; + } + yymsp[-3].minor.yy51.have = 0; + + yygotominor.yy79.have = 1; + goto doneD; + +failD0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(yymsp[-7].minor.yy33); + free(yymsp[-5].minor.yy33); + free_value(yymsp[-3].minor.yy51); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,10,&yymsp[-6].minor); + yy_destructor(yypParser,7,&yymsp[-4].minor); + yy_destructor(yypParser,8,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1087 "NCDConfigParser_parse.c" + break; + case 7: /* statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON */ +#line 293 "NCDConfigParser_parse.y" +{ + if (!yymsp[-8].minor.yy51.have || !yymsp[-5].minor.yy37.have || !yymsp[-3].minor.yy76.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-8].minor.yy51.v, yymsp[-5].minor.yy37.v); + yymsp[-8].minor.yy51.have = 0; + yymsp[-5].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yymsp[-3].minor.yy76.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-3].minor.yy76.v)) { + goto failE0; + } + yymsp[-3].minor.yy76.have = 0; + + if (yymsp[-2].minor.yy37.have) { + NCDStatement_IfAddElse(&yygotominor.yy79.v, yymsp[-2].minor.yy37.v); + yymsp[-2].minor.yy37.have = 0; + } + + yygotominor.yy79.have = 1; + goto doneE; + +failE0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(yymsp[-8].minor.yy51); + free_block(yymsp[-5].minor.yy37); + free_ifblock(yymsp[-3].minor.yy76); + free_block(yymsp[-2].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,11,&yymsp[-10].minor); + yy_destructor(yypParser,7,&yymsp[-9].minor); + yy_destructor(yypParser,8,&yymsp[-7].minor); + yy_destructor(yypParser,5,&yymsp[-6].minor); + yy_destructor(yypParser,6,&yymsp[-4].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1135 "NCDConfigParser_parse.c" + break; + case 8: /* statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON */ +#line 332 "NCDConfigParser_parse.y" +{ + if (!yymsp[-8].minor.yy51.have || !yymsp[-6].minor.yy0.str || !yymsp[-3].minor.yy37.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-8].minor.yy51.v, yymsp[-6].minor.yy0.str, NULL, yymsp[-3].minor.yy37.v)) { + goto failEA0; + } + yymsp[-8].minor.yy51.have = 0; + yymsp[-3].minor.yy37.have = 0; + + yygotominor.yy79.have = 1; + goto doneEA0; + +failEA0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(yymsp[-8].minor.yy51); + free_token(yymsp[-6].minor.yy0); + free_block(yymsp[-3].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,12,&yymsp[-10].minor); + yy_destructor(yypParser,7,&yymsp[-9].minor); + yy_destructor(yypParser,13,&yymsp[-7].minor); + yy_destructor(yypParser,8,&yymsp[-5].minor); + yy_destructor(yypParser,5,&yymsp[-4].minor); + yy_destructor(yypParser,6,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1169 "NCDConfigParser_parse.c" + break; + case 9: /* statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON */ +#line 356 "NCDConfigParser_parse.y" +{ + if (!yymsp[-10].minor.yy51.have || !yymsp[-8].minor.yy0.str || !yymsp[-6].minor.yy0.str || !yymsp[-3].minor.yy37.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-10].minor.yy51.v, yymsp[-8].minor.yy0.str, yymsp[-6].minor.yy0.str, yymsp[-3].minor.yy37.v)) { + goto failEB0; + } + yymsp[-10].minor.yy51.have = 0; + yymsp[-3].minor.yy37.have = 0; + + yygotominor.yy79.have = 1; + goto doneEB0; + +failEB0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(yymsp[-10].minor.yy51); + free_token(yymsp[-8].minor.yy0); + free_token(yymsp[-6].minor.yy0); + free_block(yymsp[-3].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,12,&yymsp[-12].minor); + yy_destructor(yypParser,7,&yymsp[-11].minor); + yy_destructor(yypParser,13,&yymsp[-9].minor); + yy_destructor(yypParser,14,&yymsp[-7].minor); + yy_destructor(yypParser,8,&yymsp[-5].minor); + yy_destructor(yypParser,5,&yymsp[-4].minor); + yy_destructor(yypParser,6,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1205 "NCDConfigParser_parse.c" + break; + case 10: /* elif_maybe ::= */ +#line 381 "NCDConfigParser_parse.y" +{ + NCDIfBlock_Init(&yygotominor.yy76.v); + yygotominor.yy76.have = 1; +} +#line 1213 "NCDConfigParser_parse.c" + break; + case 11: /* elif_maybe ::= elif */ +#line 386 "NCDConfigParser_parse.y" +{ + yygotominor.yy76 = yymsp[0].minor.yy76; +} +#line 1220 "NCDConfigParser_parse.c" + break; + case 12: /* elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE */ +#line 390 "NCDConfigParser_parse.y" +{ + if (!yymsp[-4].minor.yy51.have || !yymsp[-1].minor.yy37.have) { + goto failF0; + } + + NCDIfBlock_Init(&yygotominor.yy76.v); + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-4].minor.yy51.v, yymsp[-1].minor.yy37.v); + yymsp[-4].minor.yy51.have = 0; + yymsp[-1].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yygotominor.yy76.v, ifc)) { + goto failF1; + } + + yygotominor.yy76.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&yygotominor.yy76.v); +failF0: + yygotominor.yy76.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(yymsp[-4].minor.yy51); + free_block(yymsp[-1].minor.yy37); + yy_destructor(yypParser,15,&yymsp[-6].minor); + yy_destructor(yypParser,7,&yymsp[-5].minor); + yy_destructor(yypParser,8,&yymsp[-3].minor); + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1258 "NCDConfigParser_parse.c" + break; + case 13: /* elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif */ +#line 420 "NCDConfigParser_parse.y" +{ + if (!yymsp[-5].minor.yy51.have || !yymsp[-2].minor.yy37.have || !yymsp[0].minor.yy76.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-5].minor.yy51.v, yymsp[-2].minor.yy37.v); + yymsp[-5].minor.yy51.have = 0; + yymsp[-2].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yymsp[0].minor.yy76.v, ifc)) { + goto failG1; + } + + yygotominor.yy76.have = 1; + yygotominor.yy76.v = yymsp[0].minor.yy76.v; + yymsp[0].minor.yy76.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + yygotominor.yy76.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(yymsp[-5].minor.yy51); + free_block(yymsp[-2].minor.yy37); + free_ifblock(yymsp[0].minor.yy76); + yy_destructor(yypParser,15,&yymsp[-7].minor); + yy_destructor(yypParser,7,&yymsp[-6].minor); + yy_destructor(yypParser,8,&yymsp[-4].minor); + yy_destructor(yypParser,5,&yymsp[-3].minor); + yy_destructor(yypParser,6,&yymsp[-1].minor); +} +#line 1296 "NCDConfigParser_parse.c" + break; + case 14: /* else_maybe ::= */ +#line 450 "NCDConfigParser_parse.y" +{ + yygotominor.yy37.have = 0; +} +#line 1303 "NCDConfigParser_parse.c" + break; + case 15: /* else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE */ +#line 454 "NCDConfigParser_parse.y" +{ + yygotominor.yy37 = yymsp[-1].minor.yy37; + yy_destructor(yypParser,16,&yymsp[-3].minor); + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1313 "NCDConfigParser_parse.c" + break; + case 16: /* statements ::= statement */ +#line 458 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy79.have) { + goto failH0; + } + + NCDBlock_Init(&yygotominor.yy37.v); + + if (!NCDBlock_PrependStatement(&yygotominor.yy37.v, yymsp[0].minor.yy79.v)) { + goto failH1; + } + yymsp[0].minor.yy79.have = 0; + + yygotominor.yy37.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&yygotominor.yy37.v); +failH0: + yygotominor.yy37.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(yymsp[0].minor.yy79); +} +#line 1340 "NCDConfigParser_parse.c" + break; + case 17: /* statements ::= statement statements */ +#line 482 "NCDConfigParser_parse.y" +{ + if (!yymsp[-1].minor.yy79.have || !yymsp[0].minor.yy37.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&yymsp[0].minor.yy37.v, yymsp[-1].minor.yy79.v)) { + goto failI1; + } + yymsp[-1].minor.yy79.have = 0; + + yygotominor.yy37.have = 1; + yygotominor.yy37.v = yymsp[0].minor.yy37.v; + yymsp[0].minor.yy37.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&yygotominor.yy37.v); +failI0: + yygotominor.yy37.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(yymsp[-1].minor.yy79); + free_block(yymsp[0].minor.yy37); +} +#line 1368 "NCDConfigParser_parse.c" + break; + case 18: /* dotted_name ::= NAME */ + case 35: /* name_maybe ::= NAME */ yytestcase(yyruleno==35); +#line 507 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + yygotominor.yy33 = yymsp[0].minor.yy0.str; +} +#line 1378 "NCDConfigParser_parse.c" + break; + case 19: /* dotted_name ::= NAME DOT dotted_name */ +#line 513 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-2].minor.yy0.str) + if (!yymsp[0].minor.yy33) { + goto failJ0; + } + + if (!(yygotominor.yy33 = concat_strings(3, yymsp[-2].minor.yy0.str, ".", yymsp[0].minor.yy33))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + yygotominor.yy33 = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(yymsp[-2].minor.yy0); + free(yymsp[0].minor.yy33); + yy_destructor(yypParser,17,&yymsp[-1].minor); +} +#line 1402 "NCDConfigParser_parse.c" + break; + case 20: /* statement_args_maybe ::= */ +#line 533 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitList(&yygotominor.yy51.v); +} +#line 1410 "NCDConfigParser_parse.c" + break; + case 21: /* statement_args_maybe ::= list_contents */ + case 32: /* value ::= list */ yytestcase(yyruleno==32); + case 33: /* value ::= map */ yytestcase(yyruleno==33); +#line 538 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[0].minor.yy51; +} +#line 1419 "NCDConfigParser_parse.c" + break; + case 22: /* list_contents ::= value */ +#line 542 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy51.have) { + goto failL0; + } + + NCDValue_InitList(&yygotominor.yy51.v); + + if (!NCDValue_ListPrepend(&yygotominor.yy51.v, yymsp[0].minor.yy51.v)) { + goto failL1; + } + yymsp[0].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&yygotominor.yy51.v); +failL0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(yymsp[0].minor.yy51); +} +#line 1446 "NCDConfigParser_parse.c" + break; + case 23: /* list_contents ::= value COMMA list_contents */ +#line 566 "NCDConfigParser_parse.y" +{ + if (!yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&yymsp[0].minor.yy51.v, yymsp[-2].minor.yy51.v)) { + goto failM0; + } + yymsp[-2].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + yygotominor.yy51.v = yymsp[0].minor.yy51.v; + yymsp[0].minor.yy51.have = 0; + goto doneM; + +failM0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,18,&yymsp[-1].minor); +} +#line 1473 "NCDConfigParser_parse.c" + break; + case 24: /* list ::= CURLY_OPEN CURLY_CLOSE */ +#line 589 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitList(&yygotominor.yy51.v); + yy_destructor(yypParser,5,&yymsp[-1].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1483 "NCDConfigParser_parse.c" + break; + case 25: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */ +#line 594 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[-1].minor.yy51; + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1492 "NCDConfigParser_parse.c" + break; + case 26: /* map_contents ::= value COLON value */ +#line 598 "NCDConfigParser_parse.y" +{ + if (!yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failS0; + } + + NCDValue_InitMap(&yygotominor.yy51.v); + + if (!NCDValue_MapPrepend(&yygotominor.yy51.v, yymsp[-2].minor.yy51.v, yymsp[0].minor.yy51.v)) { + goto failS1; + } + yymsp[-2].minor.yy51.have = 0; + yymsp[0].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&yygotominor.yy51.v); +failS0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,14,&yymsp[-1].minor); +} +#line 1522 "NCDConfigParser_parse.c" + break; + case 27: /* map_contents ::= value COLON value COMMA map_contents */ +#line 624 "NCDConfigParser_parse.y" +{ + if (!yymsp[-4].minor.yy51.have || !yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&yymsp[0].minor.yy51.v, yymsp[-4].minor.yy51.v, yymsp[-2].minor.yy51.v)) { + goto failT0; + } + yymsp[-4].minor.yy51.have = 0; + yymsp[-2].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + yygotominor.yy51.v = yymsp[0].minor.yy51.v; + yymsp[0].minor.yy51.have = 0; + goto doneT; + +failT0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(yymsp[-4].minor.yy51); + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,14,&yymsp[-3].minor); + yy_destructor(yypParser,18,&yymsp[-1].minor); +} +#line 1552 "NCDConfigParser_parse.c" + break; + case 28: /* map ::= BRACKET_OPEN BRACKET_CLOSE */ +#line 649 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitMap(&yygotominor.yy51.v); + yy_destructor(yypParser,19,&yymsp[-1].minor); + yy_destructor(yypParser,20,&yymsp[0].minor); +} +#line 1562 "NCDConfigParser_parse.c" + break; + case 29: /* map ::= BRACKET_OPEN map_contents BRACKET_CLOSE */ +#line 654 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[-1].minor.yy51; + yy_destructor(yypParser,19,&yymsp[-2].minor); + yy_destructor(yypParser,20,&yymsp[0].minor); +} +#line 1571 "NCDConfigParser_parse.c" + break; + case 30: /* value ::= STRING */ +#line 658 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + if (!NCDValue_InitStringBin(&yygotominor.yy51.v, (uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len)) { + goto failU0; + } + + yygotominor.yy51.have = 1; + goto doneU; + +failU0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(yymsp[0].minor.yy0); +} +#line 1591 "NCDConfigParser_parse.c" + break; + case 31: /* value ::= dotted_name */ +#line 675 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy33) { + goto failV0; + } + + if (!NCDValue_InitVar(&yygotominor.yy51.v, yymsp[0].minor.yy33)) { + goto failV0; + } + + yygotominor.yy51.have = 1; + goto doneV; + +failV0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(yymsp[0].minor.yy33); +} +#line 1613 "NCDConfigParser_parse.c" + break; + case 34: /* name_maybe ::= */ +#line 702 "NCDConfigParser_parse.y" +{ + yygotominor.yy33 = NULL; +} +#line 1620 "NCDConfigParser_parse.c" + break; + case 36: /* process_or_template ::= PROCESS */ +#line 712 "NCDConfigParser_parse.y" +{ + yygotominor.yy12 = 0; + yy_destructor(yypParser,21,&yymsp[0].minor); +} +#line 1628 "NCDConfigParser_parse.c" + break; + case 37: /* process_or_template ::= TEMPLATE */ +#line 716 "NCDConfigParser_parse.y" +{ + yygotominor.yy12 = 1; + yy_destructor(yypParser,22,&yymsp[0].minor); +} +#line 1636 "NCDConfigParser_parse.c" + break; + default: + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +#line 125 "NCDConfigParser_parse.y" + + parser_out->syntax_error = 1; +#line 1701 "NCDConfigParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.h b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.h new file mode 100644 index 0000000..086a574 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.h @@ -0,0 +1,22 @@ +#define INCLUDE 1 +#define STRING 2 +#define INCLUDE_GUARD 3 +#define NAME 4 +#define CURLY_OPEN 5 +#define CURLY_CLOSE 6 +#define ROUND_OPEN 7 +#define ROUND_CLOSE 8 +#define SEMICOLON 9 +#define ARROW 10 +#define IF 11 +#define FOREACH 12 +#define AS 13 +#define COLON 14 +#define ELIF 15 +#define ELSE 16 +#define DOT 17 +#define COMMA 18 +#define BRACKET_OPEN 19 +#define BRACKET_CLOSE 20 +#define PROCESS 21 +#define TEMPLATE 22 diff --git a/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.out b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.out new file mode 100644 index 0000000..bdf830b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.out @@ -0,0 +1,950 @@ +State 0: + input ::= * processes + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 33 + process_or_template shift 36 + input accept + {default} reduce 1 + +State 1: + statement ::= dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + (20) statement_args_maybe ::= * + statement_args_maybe ::= * list_contents + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + statement_args_maybe shift 39 + list_contents shift 80 + list shift 87 + map shift 88 + value shift 42 + {default} reduce 20 + +State 2: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + (20) statement_args_maybe ::= * + statement_args_maybe ::= * list_contents + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + statement_args_maybe shift 48 + list_contents shift 80 + list shift 87 + map shift 88 + value shift 42 + {default} reduce 20 + +State 3: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= CURLY_OPEN * CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + list ::= CURLY_OPEN * list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + CURLY_CLOSE shift 82 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list_contents shift 43 + list shift 87 + map shift 88 + value shift 42 + +State 4: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= BRACKET_OPEN * BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + BRACKET_CLOSE shift 89 + dotted_name shift 86 + list shift 87 + map_contents shift 46 + map shift 88 + value shift 44 + +State 5: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list_contents ::= value COMMA * list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list_contents shift 81 + list shift 87 + map shift 88 + value shift 42 + +State 6: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map_contents ::= value COLON value COMMA * map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map_contents shift 84 + map shift 88 + value shift 44 + +State 7: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= value COLON * value + map_contents ::= value COLON * value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 45 + +State 8: + statement ::= IF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 51 + +State 9: + statement ::= FOREACH ROUND_OPEN * value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN * value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 57 + +State 10: + elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 69 + +State 11: + processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE processes + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 38 + dotted_name shift 31 + +State 12: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 53 + dotted_name shift 31 + +State 13: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + else_maybe ::= ELSE CURLY_OPEN * statements CURLY_CLOSE + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 67 + dotted_name shift 31 + +State 14: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 60 + dotted_name shift 31 + +State 15: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + (16) statements ::= statement * + statements ::= * statement statements + statements ::= statement * statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 94 + dotted_name shift 31 + {default} reduce 16 + +State 16: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 65 + dotted_name shift 31 + +State 17: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 71 + dotted_name shift 31 + +State 18: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= INCLUDE STRING * processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 72 + process_or_template shift 36 + {default} reduce 1 + +State 19: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= INCLUDE_GUARD STRING * processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 73 + process_or_template shift 36 + {default} reduce 1 + +State 20: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE * processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 74 + process_or_template shift 36 + {default} reduce 1 + +State 21: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif_maybe else_maybe name_maybe SEMICOLON + (10) elif_maybe ::= * + elif_maybe ::= * elif + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ELIF shift 68 + elif_maybe shift 26 + elif shift 97 + {default} reduce 10 + +State 22: + statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 40 + {default} reduce 34 + +State 23: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + dotted_name ::= NAME DOT * dotted_name + + NAME shift 41 + dotted_name shift 79 + +State 24: + statement ::= dotted_name ARROW * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + dotted_name shift 47 + +State 25: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 49 + {default} reduce 34 + +State 26: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe * else_maybe name_maybe SEMICOLON + (14) else_maybe ::= * + else_maybe ::= * ELSE CURLY_OPEN statements CURLY_CLOSE + + ELSE shift 55 + else_maybe shift 27 + {default} reduce 14 + +State 27: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 54 + {default} reduce 34 + +State 28: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 61 + {default} reduce 34 + +State 29: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 66 + {default} reduce 34 + +State 30: + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + (12) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif + + ELIF shift 68 + elif shift 98 + {default} reduce 12 + +State 31: + statement ::= dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= dotted_name * ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 1 + ARROW shift 24 + +State 32: + statement ::= FOREACH ROUND_OPEN value AS NAME * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME * COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 59 + COLON shift 62 + +State 33: + (0) input ::= processes * + + $ reduce 0 + +State 34: + processes ::= INCLUDE * STRING processes + + STRING shift 18 + +State 35: + processes ::= INCLUDE_GUARD * STRING processes + + STRING shift 19 + +State 36: + processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE processes + + NAME shift 37 + +State 37: + processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE processes + + CURLY_OPEN shift 11 + +State 38: + processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE processes + + CURLY_CLOSE shift 20 + +State 39: + statement ::= dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 22 + +State 40: + statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 77 + +State 41: + (18) dotted_name ::= NAME * + dotted_name ::= NAME * DOT dotted_name + + DOT shift 23 + {default} reduce 18 + +State 42: + (22) list_contents ::= value * + list_contents ::= value * COMMA list_contents + + COMMA shift 5 + {default} reduce 22 + +State 43: + list ::= CURLY_OPEN list_contents * CURLY_CLOSE + + CURLY_CLOSE shift 83 + +State 44: + map_contents ::= value * COLON value + map_contents ::= value * COLON value COMMA map_contents + + COLON shift 7 + +State 45: + (26) map_contents ::= value COLON value * + map_contents ::= value COLON value * COMMA map_contents + + COMMA shift 6 + {default} reduce 26 + +State 46: + map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE + + BRACKET_CLOSE shift 90 + +State 47: + statement ::= dotted_name ARROW dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 2 + +State 48: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 25 + +State 49: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 91 + +State 50: + statement ::= IF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + ROUND_OPEN shift 8 + +State 51: + statement ::= IF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + ROUND_CLOSE shift 52 + +State 52: + statement ::= IF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + CURLY_OPEN shift 12 + +State 53: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + CURLY_CLOSE shift 21 + +State 54: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe * SEMICOLON + + SEMICOLON shift 92 + +State 55: + else_maybe ::= ELSE * CURLY_OPEN statements CURLY_CLOSE + + CURLY_OPEN shift 13 + +State 56: + statement ::= FOREACH * ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH * ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 9 + +State 57: + statement ::= FOREACH ROUND_OPEN value * AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value * AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + AS shift 58 + +State 58: + statement ::= FOREACH ROUND_OPEN value AS * NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS * NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + NAME shift 32 + +State 59: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + CURLY_OPEN shift 14 + +State 60: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE name_maybe SEMICOLON + + CURLY_CLOSE shift 28 + +State 61: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 93 + +State 62: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON * NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + NAME shift 63 + +State 63: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 64 + +State 64: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + CURLY_OPEN shift 16 + +State 65: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE name_maybe SEMICOLON + + CURLY_CLOSE shift 29 + +State 66: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 95 + +State 67: + else_maybe ::= ELSE CURLY_OPEN statements * CURLY_CLOSE + + CURLY_CLOSE shift 96 + +State 68: + elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ROUND_OPEN shift 10 + +State 69: + elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ROUND_CLOSE shift 70 + +State 70: + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif + + CURLY_OPEN shift 17 + +State 71: + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif + + CURLY_CLOSE shift 30 + +State 72: + (2) processes ::= INCLUDE STRING processes * + + {default} reduce 2 + +State 73: + (3) processes ::= INCLUDE_GUARD STRING processes * + + {default} reduce 3 + +State 74: + (4) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes * + + {default} reduce 4 + +State 75: + (36) process_or_template ::= PROCESS * + + {default} reduce 36 + +State 76: + (37) process_or_template ::= TEMPLATE * + + {default} reduce 37 + +State 77: + (5) statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON * + + {default} reduce 5 + +State 78: + (35) name_maybe ::= NAME * + + {default} reduce 35 + +State 79: + (19) dotted_name ::= NAME DOT dotted_name * + + {default} reduce 19 + +State 80: + (21) statement_args_maybe ::= list_contents * + + {default} reduce 21 + +State 81: + (23) list_contents ::= value COMMA list_contents * + + {default} reduce 23 + +State 82: + (24) list ::= CURLY_OPEN CURLY_CLOSE * + + {default} reduce 24 + +State 83: + (25) list ::= CURLY_OPEN list_contents CURLY_CLOSE * + + {default} reduce 25 + +State 84: + (27) map_contents ::= value COLON value COMMA map_contents * + + {default} reduce 27 + +State 85: + (30) value ::= STRING * + + {default} reduce 30 + +State 86: + (31) value ::= dotted_name * + + {default} reduce 31 + +State 87: + (32) value ::= list * + + {default} reduce 32 + +State 88: + (33) value ::= map * + + {default} reduce 33 + +State 89: + (28) map ::= BRACKET_OPEN BRACKET_CLOSE * + + {default} reduce 28 + +State 90: + (29) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE * + + {default} reduce 29 + +State 91: + (6) statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON * + + {default} reduce 6 + +State 92: + (7) statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON * + + {default} reduce 7 + +State 93: + (8) statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON * + + {default} reduce 8 + +State 94: + (17) statements ::= statement statements * + + {default} reduce 17 + +State 95: + (9) statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON * + + {default} reduce 9 + +State 96: + (15) else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE * + + {default} reduce 15 + +State 97: + (11) elif_maybe ::= elif * + + {default} reduce 11 + +State 98: + (13) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif * + + {default} reduce 13 + +---------------------------------------------------- +Symbols: + 0: $: + 1: INCLUDE + 2: STRING + 3: INCLUDE_GUARD + 4: NAME + 5: CURLY_OPEN + 6: CURLY_CLOSE + 7: ROUND_OPEN + 8: ROUND_CLOSE + 9: SEMICOLON + 10: ARROW + 11: IF + 12: FOREACH + 13: AS + 14: COLON + 15: ELIF + 16: ELSE + 17: DOT + 18: COMMA + 19: BRACKET_OPEN + 20: BRACKET_CLOSE + 21: PROCESS + 22: TEMPLATE + 23: error: + 24: processes: INCLUDE INCLUDE_GUARD PROCESS TEMPLATE + 25: statement: NAME IF FOREACH + 26: elif_maybe: ELIF + 27: elif: ELIF + 28: else_maybe: ELSE + 29: statements: NAME IF FOREACH + 30: dotted_name: NAME + 31: statement_args_maybe: STRING NAME CURLY_OPEN BRACKET_OPEN + 32: list_contents: STRING NAME CURLY_OPEN BRACKET_OPEN + 33: list: CURLY_OPEN + 34: map_contents: STRING NAME CURLY_OPEN BRACKET_OPEN + 35: map: BRACKET_OPEN + 36: value: STRING NAME CURLY_OPEN BRACKET_OPEN + 37: name_maybe: NAME + 38: process_or_template: PROCESS TEMPLATE + 39: input: INCLUDE INCLUDE_GUARD PROCESS TEMPLATE diff --git a/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.y b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.y new file mode 100644 index 0000000..fdf89f6 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDConfigParser_parse.y @@ -0,0 +1,718 @@ +/** + * @file NCDConfigParser.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%include { + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +} + +%extra_argument { struct parser_out *parser_out } + +%token_type { struct token } + +%token_destructor { free_token($$); } + +%type processes { struct program } +%type statement { struct statement } +%type elif_maybe { struct ifblock } +%type elif { struct ifblock } +%type else_maybe { struct block } +%type statements { struct block } +%type dotted_name { char * } +%type statement_args_maybe { struct value } +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } +%type name_maybe { char * } +%type process_or_template { int } + +// mention parser_out in some destructor to a void unused variable warning +%destructor processes { (void)parser_out; free_program($$); } +%destructor statement { free_statement($$); } +%destructor elif_maybe { free_ifblock($$); } +%destructor elif { free_ifblock($$); } +%destructor else_maybe { free_block($$); } +%destructor statements { free_block($$); } +%destructor dotted_name { free($$); } +%destructor statement_args_maybe { free_value($$); } +%destructor list_contents { free_value($$); } +%destructor list { free_value($$); } +%destructor map_contents { free_value($$); } +%destructor map { free_value($$); } +%destructor value { free_value($$); } +%destructor name_maybe { free($$); } + +%stack_size 0 + +%syntax_error { + parser_out->syntax_error = 1; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= processes(A). { + ASSERT(!parser_out->have_ast) + + if (A.have) { + parser_out->have_ast = 1; + parser_out->ast = A.v; + } +} + +processes(R) ::= . { + NCDProgram prog; + NCDProgram_Init(&prog); + + R.have = 1; + R.v = prog; +} + +processes(R) ::= INCLUDE STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, A.str, A.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failA1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(A); + free_program(N); +} + +processes(R) ::= INCLUDE_GUARD STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, A.str, A.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failZ1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + R.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(A); + free_program(N); +} + +processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). { + ASSERT(A.str) + if (!B.have || !N.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, T, A.str, B.v)) { + goto failB0; + } + B.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failB1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(A); + free_block(B); + free_program(N); +} + +statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!A || !B.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) { + goto failC0; + } + B.have = 0; + + R.have = 1; + goto doneC; + +failC0: + R.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(A); + free_value(B); + free(C); +} + +statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!M || !A || !B.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) { + goto failD0; + } + B.have = 0; + + R.have = 1; + goto doneD; + +failD0: + R.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(M); + free(A); + free_value(B); + free(C); +} + +statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. { + if (!A.have || !B.have || !I.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&I.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&R.v, C, I.v)) { + goto failE0; + } + I.have = 0; + + if (E.have) { + NCDStatement_IfAddElse(&R.v, E.v); + E.have = 0; + } + + R.have = 1; + goto doneE; + +failE0: + R.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(A); + free_block(B); + free_ifblock(I); + free_block(E); + free(C); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !S.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, NULL, S.v)) { + goto failEA0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEA0; + +failEA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(A); + free_token(B); + free_block(S); + free(N); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) COLON NAME(C) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !C.str || !S.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, C.str, S.v)) { + goto failEB0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEB0; + +failEB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(A); + free_token(B); + free_token(C); + free_block(S); + free(N); +} + +elif_maybe(R) ::= . { + NCDIfBlock_Init(&R.v); + R.have = 1; +} + +elif_maybe(R) ::= elif(A). { + R = A; +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. { + if (!A.have || !B.have) { + goto failF0; + } + + NCDIfBlock_Init(&R.v); + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&R.v, ifc)) { + goto failF1; + } + + R.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&R.v); +failF0: + R.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(A); + free_block(B); +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). { + if (!A.have || !B.have || !N.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&N.v, ifc)) { + goto failG1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + R.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(A); + free_block(B); + free_ifblock(N); +} + +else_maybe(R) ::= . { + R.have = 0; +} + +else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. { + R = B; +} + +statements(R) ::= statement(A). { + if (!A.have) { + goto failH0; + } + + NCDBlock_Init(&R.v); + + if (!NCDBlock_PrependStatement(&R.v, A.v)) { + goto failH1; + } + A.have = 0; + + R.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&R.v); +failH0: + R.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(A); +} + +statements(R) ::= statement(A) statements(N). { + if (!A.have || !N.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&N.v, A.v)) { + goto failI1; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&R.v); +failI0: + R.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(A); + free_block(N); +} + +dotted_name(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +dotted_name(R) ::= NAME(A) DOT dotted_name(N). { + ASSERT(A.str) + if (!N) { + goto failJ0; + } + + if (!(R = concat_strings(3, A.str, ".", N))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + R = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(A); + free(N); +} + +statement_args_maybe(R) ::= . { + R.have = 1; + NCDValue_InitList(&R.v); +} + +statement_args_maybe(R) ::= list_contents(A). { + R = A; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValue_InitList(&R.v); + + if (!NCDValue_ListPrepend(&R.v, A.v)) { + goto failL1; + } + A.have = 0; + + R.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&R.v); +failL0: + R.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(A); +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&N.v, A.v)) { + goto failM0; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneM; + +failM0: + R.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(A); + free_value(N); +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + R.have = 1; + NCDValue_InitList(&R.v); +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValue_InitMap(&R.v); + + if (!NCDValue_MapPrepend(&R.v, A.v, B.v)) { + goto failS1; + } + A.have = 0; + B.have = 0; + + R.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&R.v); +failS0: + R.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(A); + free_value(B); +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&N.v, A.v, B.v)) { + goto failT0; + } + A.have = 0; + B.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneT; + +failT0: + R.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(A); + free_value(B); + free_value(N); +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + R.have = 1; + NCDValue_InitMap(&R.v); +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) { + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(A); +} + +value(R) ::= dotted_name(A). { + if (!A) { + goto failV0; + } + + if (!NCDValue_InitVar(&R.v, A)) { + goto failV0; + } + + R.have = 1; + goto doneV; + +failV0: + R.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} + +name_maybe(R) ::= . { + R = NULL; +} + +name_maybe(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +process_or_template(R) ::= PROCESS. { + R = 0; +} + +process_or_template(R) ::= TEMPLATE. { + R = 1; +} diff --git a/app/src/main/jni/badvpn/generated/NCDValParser_parse.c b/app/src/main/jni/badvpn/generated/NCDValParser_parse.c new file mode 100644 index 0000000..f7039cc --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDValParser_parse.c @@ -0,0 +1,1119 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +#define YYCODETYPE unsigned char +#define YYNOCODE 16 +#define YYACTIONTYPE unsigned char +#define ParseTOKENTYPE struct token +typedef union { + int yyinit; + ParseTOKENTYPE yy0; + struct value yy1; +} YYMINORTYPE; +#ifndef YYSTACKDEPTH +#define YYSTACKDEPTH 0 +#endif +#define ParseARG_SDECL struct parser_state *parser_out ; +#define ParseARG_PDECL , struct parser_state *parser_out +#define ParseARG_FETCH struct parser_state *parser_out = yypParser->parser_out +#define ParseARG_STORE yypParser->parser_out = parser_out +#define YYNSTATE 21 +#define YYNRULE 12 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static const YYACTIONTYPE yy_action[] = { + /* 0 */ 15, 21, 16, 6, 34, 1, 19, 3, 2, 15, + /* 10 */ 14, 16, 9, 11, 15, 5, 16, 7, 18, 1, + /* 20 */ 35, 20, 2, 17, 14, 15, 10, 16, 8, 12, + /* 30 */ 15, 4, 16, 7, 15, 13, 16, 8, 1, 35, + /* 40 */ 35, 2, 35, 14, +}; +static const YYCODETYPE yy_lookahead[] = { + /* 0 */ 10, 0, 12, 13, 14, 2, 3, 1, 5, 10, + /* 10 */ 7, 12, 13, 9, 10, 4, 12, 13, 6, 2, + /* 20 */ 15, 3, 5, 6, 7, 10, 11, 12, 13, 9, + /* 30 */ 10, 1, 12, 13, 10, 11, 12, 13, 2, 15, + /* 40 */ 15, 5, 15, 7, +}; +#define YY_SHIFT_USE_DFLT (-1) +#define YY_SHIFT_MAX 11 +static const signed char yy_shift_ofst[] = { + /* 0 */ 36, 3, 17, 36, 36, 36, 1, 6, 11, 30, + /* 10 */ 12, 18, +}; +#define YY_REDUCE_USE_DFLT (-11) +#define YY_REDUCE_MAX 5 +static const signed char yy_reduce_ofst[] = { + /* 0 */ -10, 4, 15, 20, 24, -1, +}; +static const YYACTIONTYPE yy_default[] = { + /* 0 */ 33, 33, 33, 33, 33, 33, 33, 22, 33, 26, + /* 10 */ 33, 33, 23, 27, 30, 31, 32, 28, 29, 24, + /* 20 */ 25, +}; +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { + "$", "COMMA", "CURLY_OPEN", "CURLY_CLOSE", + "COLON", "BRACKET_OPEN", "BRACKET_CLOSE", "STRING", + "error", "list_contents", "list", "map_contents", + "map", "value", "input", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { + /* 0 */ "input ::= value", + /* 1 */ "list_contents ::= value", + /* 2 */ "list_contents ::= value COMMA list_contents", + /* 3 */ "list ::= CURLY_OPEN CURLY_CLOSE", + /* 4 */ "list ::= CURLY_OPEN list_contents CURLY_CLOSE", + /* 5 */ "map_contents ::= value COLON value", + /* 6 */ "map_contents ::= value COLON value COMMA map_contents", + /* 7 */ "map ::= BRACKET_OPEN BRACKET_CLOSE", + /* 8 */ "map ::= BRACKET_OPEN map_contents BRACKET_CLOSE", + /* 9 */ "value ::= STRING", + /* 10 */ "value ::= list", + /* 11 */ "value ::= map", +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + /* TERMINAL Destructor */ + case 1: /* COMMA */ + case 2: /* CURLY_OPEN */ + case 3: /* CURLY_CLOSE */ + case 4: /* COLON */ + case 5: /* BRACKET_OPEN */ + case 6: /* BRACKET_CLOSE */ + case 7: /* STRING */ +{ +#line 37 "NCDValParser_parse.y" + free_token((yypminor->yy0)); +#line 377 "NCDValParser_parse.c" +} + break; + case 9: /* list_contents */ +{ +#line 47 "NCDValParser_parse.y" + (void)parser_out; +#line 384 "NCDValParser_parse.c" +} + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +#line 58 "NCDValParser_parse.y" + + if (yypMinor) { + free_token(yypMinor->yy0); + } +#line 562 "NCDValParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 14, 1 }, + { 9, 1 }, + { 9, 3 }, + { 10, 2 }, + { 10, 3 }, + { 11, 3 }, + { 11, 5 }, + { 12, 2 }, + { 12, 3 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ + case 0: /* input ::= value */ +#line 64 "NCDValParser_parse.y" +{ + if (!yymsp[0].minor.yy1.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, yymsp[0].minor.yy1.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} +#line 705 "NCDValParser_parse.c" + break; + case 1: /* list_contents ::= value */ +#line 83 "NCDValParser_parse.y" +{ + if (!yymsp[0].minor.yy1.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &yygotominor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + yygotominor.yy1.have = 1; + goto doneL; + +failL0: + yygotominor.yy1.have = 0; +doneL:; +} +#line 728 "NCDValParser_parse.c" + break; + case 2: /* list_contents ::= value COMMA list_contents */ +#line 103 "NCDValParser_parse.y" +{ + if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + yygotominor.yy1.have = 1; + yygotominor.yy1.v = yymsp[0].minor.yy1.v; + goto doneM; + +failM0: + yygotominor.yy1.have = 0; +doneM:; + yy_destructor(yypParser,1,&yymsp[-1].minor); +} +#line 751 "NCDValParser_parse.c" + break; + case 3: /* list ::= CURLY_OPEN CURLY_CLOSE */ +#line 122 "NCDValParser_parse.y" +{ + NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v); + yygotominor.yy1.have = 1; + yy_destructor(yypParser,2,&yymsp[-1].minor); + yy_destructor(yypParser,3,&yymsp[0].minor); +} +#line 761 "NCDValParser_parse.c" + break; + case 4: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */ +#line 127 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[-1].minor.yy1; + yy_destructor(yypParser,2,&yymsp[-2].minor); + yy_destructor(yypParser,3,&yymsp[0].minor); +} +#line 770 "NCDValParser_parse.c" + break; + case 5: /* map_contents ::= value COLON value */ +#line 131 "NCDValParser_parse.y" +{ + if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &yygotominor.yy1.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &yygotominor.yy1.v, yymsp[-2].minor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + yygotominor.yy1.have = 1; + goto doneS; + +failS0: + yygotominor.yy1.have = 0; +doneS:; + yy_destructor(yypParser,4,&yymsp[-1].minor); +} +#line 794 "NCDValParser_parse.c" + break; + case 6: /* map_contents ::= value COLON value COMMA map_contents */ +#line 151 "NCDValParser_parse.y" +{ + if (!yymsp[-4].minor.yy1.have || !yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-4].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + yygotominor.yy1.have = 1; + yygotominor.yy1.v = yymsp[0].minor.yy1.v; + goto doneT; + +failT0: + yygotominor.yy1.have = 0; +doneT:; + yy_destructor(yypParser,4,&yymsp[-3].minor); + yy_destructor(yypParser,1,&yymsp[-1].minor); +} +#line 818 "NCDValParser_parse.c" + break; + case 7: /* map ::= BRACKET_OPEN BRACKET_CLOSE */ +#line 170 "NCDValParser_parse.y" +{ + NCDValCons_NewMap(&parser_out->cons, &yygotominor.yy1.v); + yygotominor.yy1.have = 1; + yy_destructor(yypParser,5,&yymsp[-1].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 828 "NCDValParser_parse.c" + break; + case 8: /* map ::= BRACKET_OPEN map_contents BRACKET_CLOSE */ +#line 175 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[-1].minor.yy1; + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 837 "NCDValParser_parse.c" + break; + case 9: /* value ::= STRING */ +#line 179 "NCDValParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len, &yygotominor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + yygotominor.yy1.have = 1; + goto doneU; + +failU0: + yygotominor.yy1.have = 0; +doneU:; + free_token(yymsp[0].minor.yy0); +} +#line 857 "NCDValParser_parse.c" + break; + case 10: /* value ::= list */ + case 11: /* value ::= map */ yytestcase(yyruleno==11); +#line 196 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[0].minor.yy1; +} +#line 865 "NCDValParser_parse.c" + break; + default: + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +#line 53 "NCDValParser_parse.y" + + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +#line 930 "NCDValParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/app/src/main/jni/badvpn/generated/NCDValParser_parse.h b/app/src/main/jni/badvpn/generated/NCDValParser_parse.h new file mode 100644 index 0000000..fd19b49 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDValParser_parse.h @@ -0,0 +1,7 @@ +#define COMMA 1 +#define CURLY_OPEN 2 +#define CURLY_CLOSE 3 +#define COLON 4 +#define BRACKET_OPEN 5 +#define BRACKET_CLOSE 6 +#define STRING 7 diff --git a/app/src/main/jni/badvpn/generated/NCDValParser_parse.out b/app/src/main/jni/badvpn/generated/NCDValParser_parse.out new file mode 100644 index 0000000..9f42273 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDValParser_parse.out @@ -0,0 +1,217 @@ +State 0: + input ::= * value + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map shift 16 + value shift 6 + input accept + +State 1: + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= CURLY_OPEN * CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + list ::= CURLY_OPEN * list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + CURLY_CLOSE shift 19 + BRACKET_OPEN shift 2 + STRING shift 14 + list_contents shift 11 + list shift 15 + map shift 16 + value shift 7 + +State 2: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= BRACKET_OPEN * BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + BRACKET_CLOSE shift 17 + STRING shift 14 + list shift 15 + map_contents shift 10 + map shift 16 + value shift 8 + +State 3: + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list_contents ::= value COMMA * list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list_contents shift 12 + list shift 15 + map shift 16 + value shift 7 + +State 4: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map_contents ::= value COLON value COMMA * map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map_contents shift 13 + map shift 16 + value shift 8 + +State 5: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= value COLON * value + map_contents ::= value COLON * value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map shift 16 + value shift 9 + +State 6: + (0) input ::= value * + + $ reduce 0 + +State 7: + (1) list_contents ::= value * + list_contents ::= value * COMMA list_contents + + COMMA shift 3 + {default} reduce 1 + +State 8: + map_contents ::= value * COLON value + map_contents ::= value * COLON value COMMA map_contents + + COLON shift 5 + +State 9: + (5) map_contents ::= value COLON value * + map_contents ::= value COLON value * COMMA map_contents + + COMMA shift 4 + {default} reduce 5 + +State 10: + map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE + + BRACKET_CLOSE shift 18 + +State 11: + list ::= CURLY_OPEN list_contents * CURLY_CLOSE + + CURLY_CLOSE shift 20 + +State 12: + (2) list_contents ::= value COMMA list_contents * + + {default} reduce 2 + +State 13: + (6) map_contents ::= value COLON value COMMA map_contents * + + {default} reduce 6 + +State 14: + (9) value ::= STRING * + + {default} reduce 9 + +State 15: + (10) value ::= list * + + {default} reduce 10 + +State 16: + (11) value ::= map * + + {default} reduce 11 + +State 17: + (7) map ::= BRACKET_OPEN BRACKET_CLOSE * + + {default} reduce 7 + +State 18: + (8) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE * + + {default} reduce 8 + +State 19: + (3) list ::= CURLY_OPEN CURLY_CLOSE * + + {default} reduce 3 + +State 20: + (4) list ::= CURLY_OPEN list_contents CURLY_CLOSE * + + {default} reduce 4 + +---------------------------------------------------- +Symbols: + 0: $: + 1: COMMA + 2: CURLY_OPEN + 3: CURLY_CLOSE + 4: COLON + 5: BRACKET_OPEN + 6: BRACKET_CLOSE + 7: STRING + 8: error: + 9: list_contents: CURLY_OPEN BRACKET_OPEN STRING + 10: list: CURLY_OPEN + 11: map_contents: CURLY_OPEN BRACKET_OPEN STRING + 12: map: BRACKET_OPEN + 13: value: CURLY_OPEN BRACKET_OPEN STRING + 14: input: CURLY_OPEN BRACKET_OPEN STRING diff --git a/app/src/main/jni/badvpn/generated/NCDValParser_parse.y b/app/src/main/jni/badvpn/generated/NCDValParser_parse.y new file mode 100644 index 0000000..ced123d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/NCDValParser_parse.y @@ -0,0 +1,202 @@ +/** + * @file NCDConfigParser_parse.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// argument for passing state to parser hooks +%extra_argument { struct parser_state *parser_out } + +// type of structure representing tokens +%token_type { struct token } + +// token destructor frees extra memory allocated for tokens +%token_destructor { free_token($$); } + +// types of nonterminals +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } + +// mention parser_out in some destructor to and avoid unused variable warning +%destructor list_contents { (void)parser_out; } + +// try to dynamically grow the parse stack +%stack_size 0 + +// on syntax error, set the corresponding error flag +%syntax_error { + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= value(A). { + if (!A.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &R.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + R.have = 1; + goto doneL; + +failL0: + R.have = 0; +doneL:; +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + R.have = 1; + R.v = N.v; + goto doneM; + +failM0: + R.have = 0; +doneM:; +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + NCDValCons_NewList(&parser_out->cons, &R.v); + R.have = 1; +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &R.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + R.have = 1; + goto doneS; + +failS0: + R.have = 0; +doneS:; +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + R.have = 1; + R.v = N.v; + goto doneT; + +failT0: + R.have = 0; +doneT:; +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + NCDValCons_NewMap(&parser_out->cons, &R.v); + R.have = 1; +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; +doneU:; + free_token(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} diff --git a/app/src/main/jni/badvpn/generated/bison_BPredicate.c b/app/src/main/jni/badvpn/generated/bison_BPredicate.c new file mode 100644 index 0000000..5b5c570 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/bison_BPredicate.c @@ -0,0 +1,2163 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.7" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +/* Line 371 of yacc.c */ +#line 34 "predicate/BPredicate.y" + + +#include + +#include +#include + +#define YYLEX_PARAM scanner + +static struct predicate_node * make_constant (int val) +{ + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + return NULL; + } + + n->type = NODE_CONSTANT; + n->constant.val = val; + + return n; +} + +static struct predicate_node * make_negation (struct predicate_node *op) +{ + if (!op) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_NEG; + n->neg.op = op; + + return n; + +fail: + if (op) { + free_predicate_node(op); + } + return NULL; +} + +static struct predicate_node * make_conjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_CONJUNCT; + n->conjunct.op1 = op1; + n->conjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_disjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_DISJUNCT; + n->disjunct.op1 = op1; + n->disjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_function (char *name, struct arguments_node *args, int need_args) +{ + if (!name || (!args && need_args)) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_FUNCTION; + n->function.name = name; + n->function.args = args; + + return n; + +fail: + if (name) { + free(name); + } + if (args) { + free_arguments_node(args); + } + return NULL; +} + +static struct arguments_node * make_arguments (struct arguments_arg arg, struct arguments_node *next, int need_next) +{ + if (arg.type == ARGUMENT_INVALID || (!next && need_next)) { + goto fail; + } + + struct arguments_node *n = (struct arguments_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->arg = arg; + n->next = next; + + return n; + +fail: + free_argument(arg); + if (next) { + free_arguments_node(next); + } + return NULL; +} + +static struct arguments_arg make_argument_predicate (struct predicate_node *pr) +{ + struct arguments_arg ret; + + if (!pr) { + goto fail; + } + + ret.type = ARGUMENT_PREDICATE; + ret.predicate = pr; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +static struct arguments_arg make_argument_string (char *string) +{ + struct arguments_arg ret; + + if (!string) { + goto fail; + } + + ret.type = ARGUMENT_STRING; + ret.string = string; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + + +/* Line 371 of yacc.c */ +#line 256 "generated//bison_BPredicate.c" + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "bison_BPredicate.h". */ +#ifndef YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +# define YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + STRING = 258, + NAME = 259, + PEER1_NAME = 260, + PEER2_NAME = 261, + AND = 262, + OR = 263, + NOT = 264, + SPAR = 265, + EPAR = 266, + CONSTANT_TRUE = 267, + CONSTANT_FALSE = 268, + COMMA = 269 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 387 of yacc.c */ +#line 227 "predicate/BPredicate.y" + + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; + + +/* Line 387 of yacc.c */ +#line 322 "generated//bison_BPredicate.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void *scanner, struct predicate_node **result); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +/* Line 390 of yacc.c */ +#line 362 "generated//bison_BPredicate.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(N) (N) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 17 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 37 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 15 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 11 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 20 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 31 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 269 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 25, 28, 32, 36, 40, 45, 47, 51, + 53 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 16, 0, -1, 17, -1, 18, -1, 19, -1, 20, + -1, 21, -1, 22, -1, 23, -1, 12, -1, 13, + -1, 10, 17, 11, -1, 9, 17, -1, 17, 7, + 17, -1, 17, 8, 17, -1, 4, 10, 11, -1, + 4, 10, 24, 11, -1, 25, -1, 25, 14, 24, + -1, 17, -1, 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 276, 276, 281, 281, 281, 281, 281, 281, 284, + 288, 294, 300, 306, 312, 318, 322, 328, 332, 338, + 342 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "STRING", "NAME", "PEER1_NAME", + "PEER2_NAME", "AND", "OR", "NOT", "SPAR", "EPAR", "CONSTANT_TRUE", + "CONSTANT_FALSE", "COMMA", "$accept", "input", "predicate", "constant", + "parentheses", "neg", "conjunct", "disjunct", "function", "arguments", + "argument", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 15, 16, 17, 17, 17, 17, 17, 17, 18, + 18, 19, 20, 21, 22, 23, 23, 24, 24, 25, + 25 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 2, 3, 3, 3, 4, 1, 3, 1, + 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 9, 10, 0, 2, 3, 4, + 5, 6, 7, 8, 0, 12, 0, 1, 0, 0, + 20, 15, 19, 0, 17, 11, 13, 14, 16, 0, + 18 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 6, 22, 8, 9, 10, 11, 12, 13, 23, + 24 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -10 +static const yytype_int8 yypact[] = +{ + 19, -9, 19, 19, -10, -10, 8, -1, -10, -10, + -10, -10, -10, -10, 1, -10, 26, -10, 19, 19, + -10, -10, -1, -2, 3, -10, -10, 13, -10, 12, + -10 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -10, -10, 0, -10, -10, -10, -10, -10, -10, -3, + -10 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 7, 14, 15, 16, 20, 1, 18, 19, 17, 28, + 2, 3, 21, 4, 5, 20, 1, 29, 26, 27, + 18, 2, 3, 1, 4, 5, 30, 0, 2, 3, + 0, 4, 5, 18, 19, 0, 0, 25 +}; + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-10))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 0, 10, 2, 3, 3, 4, 7, 8, 0, 11, + 9, 10, 11, 12, 13, 3, 4, 14, 18, 19, + 7, 9, 10, 4, 12, 13, 29, -1, 9, 10, + -1, 12, 13, 7, 8, -1, -1, 11 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 4, 9, 10, 12, 13, 16, 17, 18, 19, + 20, 21, 22, 23, 10, 17, 17, 0, 7, 8, + 3, 11, 17, 24, 25, 11, 17, 17, 11, 14, + 24 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, scanner, result, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +__attribute__((__unused__)) +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static unsigned +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +#else +static unsigned +yy_location_print_ (yyo, yylocp) + FILE *yyo; + YYLTYPE const * const yylocp; +#endif +{ + unsigned res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += fprintf (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += fprintf (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += fprintf (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += fprintf (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += fprintf (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, scanner, result); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (scanner); + YYUSE (result); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, void *scanner, struct predicate_node **result) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, scanner, result) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + void *scanner; + struct predicate_node **result; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , scanner, result); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, scanner, result); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, scanner, result) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (scanner); + YYUSE (result); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 3: /* STRING */ +/* Line 1398 of yacc.c */ +#line 240 "predicate/BPredicate.y" + { + free(((*yyvaluep).text)); +}; +/* Line 1398 of yacc.c */ +#line 1386 "generated//bison_BPredicate.c" + break; + case 4: /* NAME */ +/* Line 1398 of yacc.c */ +#line 240 "predicate/BPredicate.y" + { + free(((*yyvaluep).text)); +}; +/* Line 1398 of yacc.c */ +#line 1395 "generated//bison_BPredicate.c" + break; + case 17: /* predicate */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1406 "generated//bison_BPredicate.c" + break; + case 18: /* constant */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1417 "generated//bison_BPredicate.c" + break; + case 19: /* parentheses */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1428 "generated//bison_BPredicate.c" + break; + case 20: /* neg */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1439 "generated//bison_BPredicate.c" + break; + case 21: /* conjunct */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1450 "generated//bison_BPredicate.c" + break; + case 22: /* disjunct */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1461 "generated//bison_BPredicate.c" + break; + case 23: /* function */ +/* Line 1398 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1472 "generated//bison_BPredicate.c" + break; + case 24: /* arguments */ +/* Line 1398 of yacc.c */ +#line 257 "predicate/BPredicate.y" + { + if (((*yyvaluep).arg_node)) { + free_arguments_node(((*yyvaluep).arg_node)); + } +}; +/* Line 1398 of yacc.c */ +#line 1483 "generated//bison_BPredicate.c" + break; + case 25: /* argument */ +/* Line 1398 of yacc.c */ +#line 264 "predicate/BPredicate.y" + { + free_argument(((*yyvaluep).arg_arg)); +}; +/* Line 1398 of yacc.c */ +#line 1492 "generated//bison_BPredicate.c" + break; + + default: + break; + } +} + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *scanner, struct predicate_node **result) +#else +int +yyparse (scanner, result) + void *scanner; + struct predicate_node **result; +#endif +#endif +{ +/* The lookahead symbol. */ +int yychar; + + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +static YYSTYPE yyval_default; +# define YY_INITIAL_VALUE(Value) = Value +#endif +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); + +/* Location data for the lookahead symbol. */ +YYLTYPE yylloc = yyloc_default; + + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + `yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +/* Line 1792 of yacc.c */ +#line 276 "predicate/BPredicate.y" + { + *result = (yyvsp[(1) - (1)].node); + } + break; + + case 9: +/* Line 1792 of yacc.c */ +#line 284 "predicate/BPredicate.y" + { + (yyval.node) = make_constant(1); + } + break; + + case 10: +/* Line 1792 of yacc.c */ +#line 288 "predicate/BPredicate.y" + { + (yyval.node) = make_constant(0); + } + break; + + case 11: +/* Line 1792 of yacc.c */ +#line 294 "predicate/BPredicate.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 12: +/* Line 1792 of yacc.c */ +#line 300 "predicate/BPredicate.y" + { + (yyval.node) = make_negation((yyvsp[(2) - (2)].node)); + } + break; + + case 13: +/* Line 1792 of yacc.c */ +#line 306 "predicate/BPredicate.y" + { + (yyval.node) = make_conjunction((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 14: +/* Line 1792 of yacc.c */ +#line 312 "predicate/BPredicate.y" + { + (yyval.node) = make_disjunction((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 15: +/* Line 1792 of yacc.c */ +#line 318 "predicate/BPredicate.y" + { + (yyval.node) = make_function((yyvsp[(1) - (3)].text), NULL, 0); + } + break; + + case 16: +/* Line 1792 of yacc.c */ +#line 322 "predicate/BPredicate.y" + { + (yyval.node) = make_function((yyvsp[(1) - (4)].text), (yyvsp[(3) - (4)].arg_node), 1); + } + break; + + case 17: +/* Line 1792 of yacc.c */ +#line 328 "predicate/BPredicate.y" + { + (yyval.arg_node) = make_arguments((yyvsp[(1) - (1)].arg_arg), NULL, 0); + } + break; + + case 18: +/* Line 1792 of yacc.c */ +#line 332 "predicate/BPredicate.y" + { + (yyval.arg_node) = make_arguments((yyvsp[(1) - (3)].arg_arg), (yyvsp[(3) - (3)].arg_node), 1); + } + break; + + case 19: +/* Line 1792 of yacc.c */ +#line 338 "predicate/BPredicate.y" + { + (yyval.arg_arg) = make_argument_predicate((yyvsp[(1) - (1)].node)); + } + break; + + case 20: +/* Line 1792 of yacc.c */ +#line 342 "predicate/BPredicate.y" + { + (yyval.arg_arg) = make_argument_string((yyvsp[(1) - (1)].text)); + } + break; + + +/* Line 1792 of yacc.c */ +#line 1927 "generated//bison_BPredicate.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, scanner, result, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, scanner, result, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, scanner, result); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[1] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, scanner, result); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, scanner, result, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, scanner, result); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, scanner, result); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + diff --git a/app/src/main/jni/badvpn/generated/bison_BPredicate.h b/app/src/main/jni/badvpn/generated/bison_BPredicate.h new file mode 100644 index 0000000..ccc96b8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/bison_BPredicate.h @@ -0,0 +1,114 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +# define YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + STRING = 258, + NAME = 259, + PEER1_NAME = 260, + PEER2_NAME = 261, + AND = 262, + OR = 263, + NOT = 264, + SPAR = 265, + EPAR = 266, + CONSTANT_TRUE = 267, + CONSTANT_FALSE = 268, + COMMA = 269 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 2058 of yacc.c */ +#line 227 "predicate/BPredicate.y" + + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; + + +/* Line 2058 of yacc.c */ +#line 80 "generated//bison_BPredicate.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void *scanner, struct predicate_node **result); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED */ diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BArpProbe.h b/app/src/main/jni/badvpn/generated/blog_channel_BArpProbe.h new file mode 100644 index 0000000..f168e63 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BArpProbe.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BArpProbe diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BConnection.h b/app/src/main/jni/badvpn/generated/blog_channel_BConnection.h new file mode 100644 index 0000000..8447db0 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BConnection diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClient.h b/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClient.h new file mode 100644 index 0000000..aacf34d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDHCPClient diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClientCore.h b/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClientCore.h new file mode 100644 index 0000000..64fc74d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BDHCPClientCore.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDHCPClientCore diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BDatagram.h b/app/src/main/jni/badvpn/generated/blog_channel_BDatagram.h new file mode 100644 index 0000000..d95cf24 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BDatagram.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDatagram diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BEncryption.h b/app/src/main/jni/badvpn/generated/blog_channel_BEncryption.h new file mode 100644 index 0000000..6991f37 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BEncryption.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BEncryption diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BInputProcess.h b/app/src/main/jni/badvpn/generated/blog_channel_BInputProcess.h new file mode 100644 index 0000000..f65f715 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BInputProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BInputProcess diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BLockReactor.h b/app/src/main/jni/badvpn/generated/blog_channel_BLockReactor.h new file mode 100644 index 0000000..5aab6d4 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BLockReactor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BLockReactor diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BNetwork.h b/app/src/main/jni/badvpn/generated/blog_channel_BNetwork.h new file mode 100644 index 0000000..c5e3bc1 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BNetwork.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BNetwork diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BPredicate.h b/app/src/main/jni/badvpn/generated/blog_channel_BPredicate.h new file mode 100644 index 0000000..1a683f1 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BPredicate.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BPredicate diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BProcess.h b/app/src/main/jni/badvpn/generated/blog_channel_BProcess.h new file mode 100644 index 0000000..e11e5a6 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BProcess diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BReactor.h b/app/src/main/jni/badvpn/generated/blog_channel_BReactor.h new file mode 100644 index 0000000..d111dc7 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BReactor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BReactor diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BSSLConnection.h b/app/src/main/jni/badvpn/generated/blog_channel_BSSLConnection.h new file mode 100644 index 0000000..bd55826 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BSSLConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSSLConnection diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BSignal.h b/app/src/main/jni/badvpn/generated/blog_channel_BSignal.h new file mode 100644 index 0000000..2820ebc --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSignal diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h b/app/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h new file mode 100644 index 0000000..72086fa --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSocksClient diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BTap.h b/app/src/main/jni/badvpn/generated/blog_channel_BTap.h new file mode 100644 index 0000000..ab3e951 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BTap.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTap diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BThreadSignal.h b/app/src/main/jni/badvpn/generated/blog_channel_BThreadSignal.h new file mode 100644 index 0000000..f39fc36 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BThreadSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BThreadSignal diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BThreadWork.h b/app/src/main/jni/badvpn/generated/blog_channel_BThreadWork.h new file mode 100644 index 0000000..f68383c --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BThreadWork.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BThreadWork diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BTime.h b/app/src/main/jni/badvpn/generated/blog_channel_BTime.h new file mode 100644 index 0000000..b323ee7 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BTime.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTime diff --git a/app/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h b/app/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h new file mode 100644 index 0000000..914b21b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BUnixSignal diff --git a/app/src/main/jni/badvpn/generated/blog_channel_DPReceive.h b/app/src/main/jni/badvpn/generated/blog_channel_DPReceive.h new file mode 100644 index 0000000..99889b5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_DPReceive.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DPReceive diff --git a/app/src/main/jni/badvpn/generated/blog_channel_DPRelay.h b/app/src/main/jni/badvpn/generated/blog_channel_DPRelay.h new file mode 100644 index 0000000..bc0153b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_DPRelay.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DPRelay diff --git a/app/src/main/jni/badvpn/generated/blog_channel_DataProto.h b/app/src/main/jni/badvpn/generated/blog_channel_DataProto.h new file mode 100644 index 0000000..a6f900a --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_DataProto.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DataProto diff --git a/app/src/main/jni/badvpn/generated/blog_channel_DatagramPeerIO.h b/app/src/main/jni/badvpn/generated/blog_channel_DatagramPeerIO.h new file mode 100644 index 0000000..16e37b5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_DatagramPeerIO.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DatagramPeerIO diff --git a/app/src/main/jni/badvpn/generated/blog_channel_FragmentProtoAssembler.h b/app/src/main/jni/badvpn/generated/blog_channel_FragmentProtoAssembler.h new file mode 100644 index 0000000..25289ef --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_FragmentProtoAssembler.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_FragmentProtoAssembler diff --git a/app/src/main/jni/badvpn/generated/blog_channel_FrameDecider.h b/app/src/main/jni/badvpn/generated/blog_channel_FrameDecider.h new file mode 100644 index 0000000..5dbf3c4 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_FrameDecider.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_FrameDecider diff --git a/app/src/main/jni/badvpn/generated/blog_channel_LineBuffer.h b/app/src/main/jni/badvpn/generated/blog_channel_LineBuffer.h new file mode 100644 index 0000000..4286a74 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_LineBuffer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_LineBuffer diff --git a/app/src/main/jni/badvpn/generated/blog_channel_Listener.h b/app/src/main/jni/badvpn/generated/blog_channel_Listener.h new file mode 100644 index 0000000..f61bfb3 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_Listener.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_Listener diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDBuildProgram.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDBuildProgram.h new file mode 100644 index 0000000..1a6cdf9 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDBuildProgram.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDBuildProgram diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigParser.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigParser.h new file mode 100644 index 0000000..92d98d0 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDConfigParser diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigTokenizer.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigTokenizer.h new file mode 100644 index 0000000..0b3b689 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDConfigTokenizer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDConfigTokenizer diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDIfConfig.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDIfConfig.h new file mode 100644 index 0000000..91bdbda --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDIfConfig.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDIfConfig diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDInterfaceMonitor.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDInterfaceMonitor.h new file mode 100644 index 0000000..22c0f8d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDInterfaceMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDInterfaceMonitor diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleIndex.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleIndex.h new file mode 100644 index 0000000..3487664 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleIndex.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDModuleIndex diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleProcess.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleProcess.h new file mode 100644 index 0000000..db34dcc --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDModuleProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDModuleProcess diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDPlaceholderDb.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDPlaceholderDb.h new file mode 100644 index 0000000..f1f1db2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDPlaceholderDb.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDPlaceholderDb diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDRequest.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDRequest.h new file mode 100644 index 0000000..5edc18a --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDRequest.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRequest diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDRequestClient.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDRequestClient.h new file mode 100644 index 0000000..1e696d8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDRequestClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRequestClient diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDRfkillMonitor.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDRfkillMonitor.h new file mode 100644 index 0000000..56eba88 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDRfkillMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRfkillMonitor diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevCache.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevCache.h new file mode 100644 index 0000000..088fc9b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevCache.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevCache diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevManager.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevManager.h new file mode 100644 index 0000000..e9d6375 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevManager.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevManager diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitor.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitor.h new file mode 100644 index 0000000..bd93249 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevMonitor diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitorParser.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitorParser.h new file mode 100644 index 0000000..a7d560f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDUdevMonitorParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevMonitorParser diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDVal.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDVal.h new file mode 100644 index 0000000..f2b67c2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDVal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDVal diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDValGenerator.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDValGenerator.h new file mode 100644 index 0000000..193826b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDValGenerator.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDValGenerator diff --git a/app/src/main/jni/badvpn/generated/blog_channel_NCDValParser.h b/app/src/main/jni/badvpn/generated/blog_channel_NCDValParser.h new file mode 100644 index 0000000..1d44acb --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_NCDValParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDValParser diff --git a/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSink.h b/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSink.h new file mode 100644 index 0000000..b70b61c --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSink.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PRStreamSink diff --git a/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSource.h b/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSource.h new file mode 100644 index 0000000..e16d93d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_PRStreamSource.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PRStreamSource diff --git a/app/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h b/app/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h new file mode 100644 index 0000000..fbfa5d8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PacketProtoDecoder diff --git a/app/src/main/jni/badvpn/generated/blog_channel_PasswordListener.h b/app/src/main/jni/badvpn/generated/blog_channel_PasswordListener.h new file mode 100644 index 0000000..6ff0bb5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_PasswordListener.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PasswordListener diff --git a/app/src/main/jni/badvpn/generated/blog_channel_PeerChat.h b/app/src/main/jni/badvpn/generated/blog_channel_PeerChat.h new file mode 100644 index 0000000..cadf230 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_PeerChat.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PeerChat diff --git a/app/src/main/jni/badvpn/generated/blog_channel_SPProtoDecoder.h b/app/src/main/jni/badvpn/generated/blog_channel_SPProtoDecoder.h new file mode 100644 index 0000000..09bf259 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_SPProtoDecoder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SPProtoDecoder diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ServerConnection.h b/app/src/main/jni/badvpn/generated/blog_channel_ServerConnection.h new file mode 100644 index 0000000..faea1dd --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ServerConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ServerConnection diff --git a/app/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h b/app/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h new file mode 100644 index 0000000..6ba39ae --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SocksUdpGwClient diff --git a/app/src/main/jni/badvpn/generated/blog_channel_StreamPeerIO.h b/app/src/main/jni/badvpn/generated/blog_channel_StreamPeerIO.h new file mode 100644 index 0000000..0359736 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_StreamPeerIO.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_StreamPeerIO diff --git a/app/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h b/app/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h new file mode 100644 index 0000000..8530376 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_UdpGwClient diff --git a/app/src/main/jni/badvpn/generated/blog_channel_addr.h b/app/src/main/jni/badvpn/generated/blog_channel_addr.h new file mode 100644 index 0000000..512db28 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_addr diff --git a/app/src/main/jni/badvpn/generated/blog_channel_client.h b/app/src/main/jni/badvpn/generated/blog_channel_client.h new file mode 100644 index 0000000..c851b77 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_client.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_client diff --git a/app/src/main/jni/badvpn/generated/blog_channel_dostest_attacker.h b/app/src/main/jni/badvpn/generated/blog_channel_dostest_attacker.h new file mode 100644 index 0000000..b267c8f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_dostest_attacker.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_dostest_attacker diff --git a/app/src/main/jni/badvpn/generated/blog_channel_dostest_server.h b/app/src/main/jni/badvpn/generated/blog_channel_dostest_server.h new file mode 100644 index 0000000..8d3988e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_dostest_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_dostest_server diff --git a/app/src/main/jni/badvpn/generated/blog_channel_flooder.h b/app/src/main/jni/badvpn/generated/blog_channel_flooder.h new file mode 100644 index 0000000..94f595e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_flooder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_flooder diff --git a/app/src/main/jni/badvpn/generated/blog_channel_lwip.h b/app/src/main/jni/badvpn/generated/blog_channel_lwip.h new file mode 100644 index 0000000..fb5687d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_lwip.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_lwip diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd.h new file mode 100644 index 0000000..9bf2956 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_alias.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_alias.h new file mode 100644 index 0000000..5b52bf2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_alias.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_alias diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_arithmetic.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_arithmetic.h new file mode 100644 index 0000000..66c08a8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_arithmetic.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_arithmetic diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_assert.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_assert.h new file mode 100644 index 0000000..21e4d41 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_assert.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_assert diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_backtrack.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_backtrack.h new file mode 100644 index 0000000..ea669f7 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_backtrack.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_backtrack diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_blocker.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_blocker.h new file mode 100644 index 0000000..a897b9f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_blocker.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_blocker diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_buffer.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_buffer.h new file mode 100644 index 0000000..64e4433 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_buffer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_buffer diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_call2.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_call2.h new file mode 100644 index 0000000..4b64608 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_call2.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_call2 diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_choose.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_choose.h new file mode 100644 index 0000000..a915036 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_choose.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_choose diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_concat.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_concat.h new file mode 100644 index 0000000..8c54ccb --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_concat.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_concat diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_daemon.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_daemon.h new file mode 100644 index 0000000..0a3ae3f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_daemon.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_daemon diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend.h new file mode 100644 index 0000000..ae1ff8e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_depend diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend_scope.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend_scope.h new file mode 100644 index 0000000..1168714 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_depend_scope.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_depend_scope diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_dynamic_depend.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_dynamic_depend.h new file mode 100644 index 0000000..7ff305e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_dynamic_depend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_dynamic_depend diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_exit.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_exit.h new file mode 100644 index 0000000..2d2e3af --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_exit.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_exit diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_explode.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_explode.h new file mode 100644 index 0000000..b7dc820 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_explode.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_explode diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_file.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_file.h new file mode 100644 index 0000000..6cfa5a5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_file.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_file diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_file_open.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_file_open.h new file mode 100644 index 0000000..dd4ecb5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_file_open.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_file_open diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_foreach.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_foreach.h new file mode 100644 index 0000000..430b229 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_foreach.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_foreach diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_from_string.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_from_string.h new file mode 100644 index 0000000..e409fff --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_from_string.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_from_string diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_getargs.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_getargs.h new file mode 100644 index 0000000..da7631d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_getargs.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_getargs diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_getenv.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_getenv.h new file mode 100644 index 0000000..4f29021 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_getenv.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_getenv diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_if.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_if.h new file mode 100644 index 0000000..11a09a2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_if.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_if diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_imperative.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_imperative.h new file mode 100644 index 0000000..362df87 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_imperative.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_imperative diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_implode.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_implode.h new file mode 100644 index 0000000..5bb66d5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_implode.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_implode diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_index.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_index.h new file mode 100644 index 0000000..666bbe9 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_index.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_index diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_list.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_list.h new file mode 100644 index 0000000..f153be7 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_list.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_list diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_load_module.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_load_module.h new file mode 100644 index 0000000..c27dddb --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_load_module.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_load_module diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_log.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_log.h new file mode 100644 index 0000000..9ae2dc9 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_log.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_log diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_log_msg.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_log_msg.h new file mode 100644 index 0000000..9e51b7e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_log_msg.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_log_msg diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_logical.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_logical.h new file mode 100644 index 0000000..688453d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_logical.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_logical diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_multidepend.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_multidepend.h new file mode 100644 index 0000000..a82953d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_multidepend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_multidepend diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_badvpn.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_badvpn.h new file mode 100644 index 0000000..c9964c1 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_badvpn.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_badvpn diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_rfkill.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_rfkill.h new file mode 100644 index 0000000..e69896f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_rfkill.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_rfkill diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitdevice.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitdevice.h new file mode 100644 index 0000000..63c4f24 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitdevice.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_waitdevice diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitlink.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitlink.h new file mode 100644 index 0000000..96244c0 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_waitlink.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_waitlink diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_wpa_supplicant.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_wpa_supplicant.h new file mode 100644 index 0000000..22572d3 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_backend_wpa_supplicant.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_wpa_supplicant diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_dns.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_dns.h new file mode 100644 index 0000000..01c3744 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_dns.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_dns diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_iptables.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_iptables.h new file mode 100644 index 0000000..42e2382 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_iptables.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_iptables diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr.h new file mode 100644 index 0000000..75bcb24 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_addr diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr_in_network.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr_in_network.h new file mode 100644 index 0000000..41f2df2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_addr_in_network.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_addr_in_network diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_arp_probe.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_arp_probe.h new file mode 100644 index 0000000..18f7c78 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_arp_probe.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_arp_probe diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_dhcp.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_dhcp.h new file mode 100644 index 0000000..51fa61f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_dhcp.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_dhcp diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_route.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_route.h new file mode 100644 index 0000000..e181a90 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv4_route.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_route diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr.h new file mode 100644 index 0000000..bd6bd10 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_addr diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr_in_network.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr_in_network.h new file mode 100644 index 0000000..ba33921 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_addr_in_network.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_addr_in_network diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_route.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_route.h new file mode 100644 index 0000000..b72e4d3 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_route.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_route diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h new file mode 100644 index 0000000..ff7d6e1 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_up.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_up.h new file mode 100644 index 0000000..7acdede --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_up.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_up diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_watch_interfaces.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_watch_interfaces.h new file mode 100644 index 0000000..7fc078f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_net_watch_interfaces.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_watch_interfaces diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_netmask.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_netmask.h new file mode 100644 index 0000000..10993f0 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_netmask.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_netmask diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_ondemand.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_ondemand.h new file mode 100644 index 0000000..c7a0578 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_ondemand.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_ondemand diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_parse.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_parse.h new file mode 100644 index 0000000..672155b --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_parse.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_parse diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_print.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_print.h new file mode 100644 index 0000000..22638f3 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_print.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_print diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_process_manager.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_process_manager.h new file mode 100644 index 0000000..627ba0e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_process_manager.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_process_manager diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_reboot.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_reboot.h new file mode 100644 index 0000000..0e31d55 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_reboot.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_reboot diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_ref.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_ref.h new file mode 100644 index 0000000..4f9f24a --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_ref.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_ref diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_regex_match.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_regex_match.h new file mode 100644 index 0000000..3081347 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_regex_match.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_regex_match diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_request.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_request.h new file mode 100644 index 0000000..00103ea --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_request.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_request diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_run.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_run.h new file mode 100644 index 0000000..036a93e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_run.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_run diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_runonce.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_runonce.h new file mode 100644 index 0000000..2e54452 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_runonce.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_runonce diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sleep.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sleep.h new file mode 100644 index 0000000..fb6c7fe --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sleep.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sleep diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_socket.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_socket.h new file mode 100644 index 0000000..3c1f0c4 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_socket.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_socket diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_spawn.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_spawn.h new file mode 100644 index 0000000..b9b3b24 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_spawn.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_spawn diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_strcmp.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_strcmp.h new file mode 100644 index 0000000..6ef09ad --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_strcmp.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_strcmp diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_substr.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_substr.h new file mode 100644 index 0000000..691ad0e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_substr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_substr diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_evdev.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_evdev.h new file mode 100644 index 0000000..4a7244e --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_evdev.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_evdev diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_client.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_client.h new file mode 100644 index 0000000..ce0f9e4 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_client.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_request_client diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_server.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_server.h new file mode 100644 index 0000000..1197958 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_request_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_request_server diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_start_process.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_start_process.h new file mode 100644 index 0000000..45c2edc --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_start_process.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_start_process diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_directory.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_directory.h new file mode 100644 index 0000000..e190da5 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_directory.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_directory diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_input.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_input.h new file mode 100644 index 0000000..b899555 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_input.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_input diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_usb.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_usb.h new file mode 100644 index 0000000..bc5102a --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_sys_watch_usb.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_usb diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_timer.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_timer.h new file mode 100644 index 0000000..beaa73d --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_timer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_timer diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_to_string.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_to_string.h new file mode 100644 index 0000000..41cd8b9 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_to_string.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_to_string diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_try.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_try.h new file mode 100644 index 0000000..bb76c68 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_try.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_try diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_value.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_value.h new file mode 100644 index 0000000..fa624e8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_value.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_value diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_valuemetic.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_valuemetic.h new file mode 100644 index 0000000..385d2bb --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_valuemetic.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_valuemetic diff --git a/app/src/main/jni/badvpn/generated/blog_channel_ncd_var.h b/app/src/main/jni/badvpn/generated/blog_channel_ncd_var.h new file mode 100644 index 0000000..fa5c0c4 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_ncd_var.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_var diff --git a/app/src/main/jni/badvpn/generated/blog_channel_nsskey.h b/app/src/main/jni/badvpn/generated/blog_channel_nsskey.h new file mode 100644 index 0000000..66e6a72 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_nsskey.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_nsskey diff --git a/app/src/main/jni/badvpn/generated/blog_channel_server.h b/app/src/main/jni/badvpn/generated/blog_channel_server.h new file mode 100644 index 0000000..acb3ed0 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_server diff --git a/app/src/main/jni/badvpn/generated/blog_channel_tun2socks.h b/app/src/main/jni/badvpn/generated/blog_channel_tun2socks.h new file mode 100644 index 0000000..21c1ce2 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_tun2socks.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_tun2socks diff --git a/app/src/main/jni/badvpn/generated/blog_channel_udpgw.h b/app/src/main/jni/badvpn/generated/blog_channel_udpgw.h new file mode 100644 index 0000000..504a352 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channel_udpgw.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_udpgw diff --git a/app/src/main/jni/badvpn/generated/blog_channels_defines.h b/app/src/main/jni/badvpn/generated/blog_channels_defines.h new file mode 100644 index 0000000..d89dc86 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channels_defines.h @@ -0,0 +1,146 @@ +#define BLOG_CHANNEL_server 0 +#define BLOG_CHANNEL_client 1 +#define BLOG_CHANNEL_flooder 2 +#define BLOG_CHANNEL_tun2socks 3 +#define BLOG_CHANNEL_ncd 4 +#define BLOG_CHANNEL_ncd_var 5 +#define BLOG_CHANNEL_ncd_list 6 +#define BLOG_CHANNEL_ncd_depend 7 +#define BLOG_CHANNEL_ncd_multidepend 8 +#define BLOG_CHANNEL_ncd_dynamic_depend 9 +#define BLOG_CHANNEL_ncd_concat 10 +#define BLOG_CHANNEL_ncd_if 11 +#define BLOG_CHANNEL_ncd_strcmp 12 +#define BLOG_CHANNEL_ncd_regex_match 13 +#define BLOG_CHANNEL_ncd_logical 14 +#define BLOG_CHANNEL_ncd_sleep 15 +#define BLOG_CHANNEL_ncd_print 16 +#define BLOG_CHANNEL_ncd_blocker 17 +#define BLOG_CHANNEL_ncd_run 18 +#define BLOG_CHANNEL_ncd_runonce 19 +#define BLOG_CHANNEL_ncd_daemon 20 +#define BLOG_CHANNEL_ncd_spawn 21 +#define BLOG_CHANNEL_ncd_imperative 22 +#define BLOG_CHANNEL_ncd_ref 23 +#define BLOG_CHANNEL_ncd_index 24 +#define BLOG_CHANNEL_ncd_alias 25 +#define BLOG_CHANNEL_ncd_process_manager 26 +#define BLOG_CHANNEL_ncd_ondemand 27 +#define BLOG_CHANNEL_ncd_foreach 28 +#define BLOG_CHANNEL_ncd_choose 29 +#define BLOG_CHANNEL_ncd_net_backend_waitdevice 30 +#define BLOG_CHANNEL_ncd_net_backend_waitlink 31 +#define BLOG_CHANNEL_ncd_net_backend_badvpn 32 +#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 33 +#define BLOG_CHANNEL_ncd_net_backend_rfkill 34 +#define BLOG_CHANNEL_ncd_net_up 35 +#define BLOG_CHANNEL_ncd_net_dns 36 +#define BLOG_CHANNEL_ncd_net_iptables 37 +#define BLOG_CHANNEL_ncd_net_ipv4_addr 38 +#define BLOG_CHANNEL_ncd_net_ipv4_route 39 +#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 40 +#define BLOG_CHANNEL_ncd_net_ipv4_arp_probe 41 +#define BLOG_CHANNEL_ncd_net_watch_interfaces 42 +#define BLOG_CHANNEL_ncd_sys_watch_input 43 +#define BLOG_CHANNEL_ncd_sys_watch_usb 44 +#define BLOG_CHANNEL_ncd_sys_evdev 45 +#define BLOG_CHANNEL_ncd_sys_watch_directory 46 +#define BLOG_CHANNEL_StreamPeerIO 47 +#define BLOG_CHANNEL_DatagramPeerIO 48 +#define BLOG_CHANNEL_BReactor 49 +#define BLOG_CHANNEL_BSignal 50 +#define BLOG_CHANNEL_FragmentProtoAssembler 51 +#define BLOG_CHANNEL_BPredicate 52 +#define BLOG_CHANNEL_ServerConnection 53 +#define BLOG_CHANNEL_Listener 54 +#define BLOG_CHANNEL_DataProto 55 +#define BLOG_CHANNEL_FrameDecider 56 +#define BLOG_CHANNEL_BSocksClient 57 +#define BLOG_CHANNEL_BDHCPClientCore 58 +#define BLOG_CHANNEL_BDHCPClient 59 +#define BLOG_CHANNEL_NCDIfConfig 60 +#define BLOG_CHANNEL_BUnixSignal 61 +#define BLOG_CHANNEL_BProcess 62 +#define BLOG_CHANNEL_PRStreamSink 63 +#define BLOG_CHANNEL_PRStreamSource 64 +#define BLOG_CHANNEL_PacketProtoDecoder 65 +#define BLOG_CHANNEL_DPRelay 66 +#define BLOG_CHANNEL_BThreadWork 67 +#define BLOG_CHANNEL_DPReceive 68 +#define BLOG_CHANNEL_BInputProcess 69 +#define BLOG_CHANNEL_NCDUdevMonitorParser 70 +#define BLOG_CHANNEL_NCDUdevMonitor 71 +#define BLOG_CHANNEL_NCDUdevCache 72 +#define BLOG_CHANNEL_NCDUdevManager 73 +#define BLOG_CHANNEL_BTime 74 +#define BLOG_CHANNEL_BEncryption 75 +#define BLOG_CHANNEL_SPProtoDecoder 76 +#define BLOG_CHANNEL_LineBuffer 77 +#define BLOG_CHANNEL_BTap 78 +#define BLOG_CHANNEL_lwip 79 +#define BLOG_CHANNEL_NCDConfigTokenizer 80 +#define BLOG_CHANNEL_NCDConfigParser 81 +#define BLOG_CHANNEL_NCDValParser 82 +#define BLOG_CHANNEL_nsskey 83 +#define BLOG_CHANNEL_addr 84 +#define BLOG_CHANNEL_PasswordListener 85 +#define BLOG_CHANNEL_NCDInterfaceMonitor 86 +#define BLOG_CHANNEL_NCDRfkillMonitor 87 +#define BLOG_CHANNEL_udpgw 88 +#define BLOG_CHANNEL_UdpGwClient 89 +#define BLOG_CHANNEL_SocksUdpGwClient 90 +#define BLOG_CHANNEL_BNetwork 91 +#define BLOG_CHANNEL_BConnection 92 +#define BLOG_CHANNEL_BSSLConnection 93 +#define BLOG_CHANNEL_BDatagram 94 +#define BLOG_CHANNEL_PeerChat 95 +#define BLOG_CHANNEL_BArpProbe 96 +#define BLOG_CHANNEL_NCDModuleIndex 97 +#define BLOG_CHANNEL_NCDModuleProcess 98 +#define BLOG_CHANNEL_NCDValGenerator 99 +#define BLOG_CHANNEL_ncd_from_string 100 +#define BLOG_CHANNEL_ncd_to_string 101 +#define BLOG_CHANNEL_ncd_value 102 +#define BLOG_CHANNEL_ncd_try 103 +#define BLOG_CHANNEL_ncd_sys_request_server 104 +#define BLOG_CHANNEL_NCDRequest 105 +#define BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr 106 +#define BLOG_CHANNEL_NCDRequestClient 107 +#define BLOG_CHANNEL_ncd_request 108 +#define BLOG_CHANNEL_ncd_sys_request_client 109 +#define BLOG_CHANNEL_ncd_exit 110 +#define BLOG_CHANNEL_ncd_getargs 111 +#define BLOG_CHANNEL_ncd_arithmetic 112 +#define BLOG_CHANNEL_ncd_parse 113 +#define BLOG_CHANNEL_ncd_valuemetic 114 +#define BLOG_CHANNEL_ncd_file 115 +#define BLOG_CHANNEL_ncd_netmask 116 +#define BLOG_CHANNEL_ncd_implode 117 +#define BLOG_CHANNEL_ncd_call2 118 +#define BLOG_CHANNEL_ncd_assert 119 +#define BLOG_CHANNEL_ncd_reboot 120 +#define BLOG_CHANNEL_ncd_explode 121 +#define BLOG_CHANNEL_NCDPlaceholderDb 122 +#define BLOG_CHANNEL_NCDVal 123 +#define BLOG_CHANNEL_ncd_net_ipv6_addr 124 +#define BLOG_CHANNEL_ncd_net_ipv6_route 125 +#define BLOG_CHANNEL_ncd_net_ipv4_addr_in_network 126 +#define BLOG_CHANNEL_ncd_net_ipv6_addr_in_network 127 +#define BLOG_CHANNEL_dostest_server 128 +#define BLOG_CHANNEL_dostest_attacker 129 +#define BLOG_CHANNEL_ncd_timer 130 +#define BLOG_CHANNEL_ncd_file_open 131 +#define BLOG_CHANNEL_ncd_backtrack 132 +#define BLOG_CHANNEL_ncd_socket 133 +#define BLOG_CHANNEL_ncd_depend_scope 134 +#define BLOG_CHANNEL_ncd_substr 135 +#define BLOG_CHANNEL_ncd_sys_start_process 136 +#define BLOG_CHANNEL_NCDBuildProgram 137 +#define BLOG_CHANNEL_ncd_log 138 +#define BLOG_CHANNEL_ncd_log_msg 139 +#define BLOG_CHANNEL_ncd_buffer 140 +#define BLOG_CHANNEL_ncd_getenv 141 +#define BLOG_CHANNEL_BThreadSignal 142 +#define BLOG_CHANNEL_BLockReactor 143 +#define BLOG_CHANNEL_ncd_load_module 144 +#define BLOG_NUM_CHANNELS 145 diff --git a/app/src/main/jni/badvpn/generated/blog_channels_list.h b/app/src/main/jni/badvpn/generated/blog_channels_list.h new file mode 100644 index 0000000..c903c0f --- /dev/null +++ b/app/src/main/jni/badvpn/generated/blog_channels_list.h @@ -0,0 +1,145 @@ +{"server", 4}, +{"client", 4}, +{"flooder", 4}, +{"tun2socks", 4}, +{"ncd", 4}, +{"ncd_var", 4}, +{"ncd_list", 4}, +{"ncd_depend", 4}, +{"ncd_multidepend", 4}, +{"ncd_dynamic_depend", 4}, +{"ncd_concat", 4}, +{"ncd_if", 4}, +{"ncd_strcmp", 4}, +{"ncd_regex_match", 4}, +{"ncd_logical", 4}, +{"ncd_sleep", 4}, +{"ncd_print", 4}, +{"ncd_blocker", 4}, +{"ncd_run", 4}, +{"ncd_runonce", 4}, +{"ncd_daemon", 4}, +{"ncd_spawn", 4}, +{"ncd_imperative", 4}, +{"ncd_ref", 4}, +{"ncd_index", 4}, +{"ncd_alias", 4}, +{"ncd_process_manager", 4}, +{"ncd_ondemand", 4}, +{"ncd_foreach", 4}, +{"ncd_choose", 4}, +{"ncd_net_backend_waitdevice", 4}, +{"ncd_net_backend_waitlink", 4}, +{"ncd_net_backend_badvpn", 4}, +{"ncd_net_backend_wpa_supplicant", 4}, +{"ncd_net_backend_rfkill", 4}, +{"ncd_net_up", 4}, +{"ncd_net_dns", 4}, +{"ncd_net_iptables", 4}, +{"ncd_net_ipv4_addr", 4}, +{"ncd_net_ipv4_route", 4}, +{"ncd_net_ipv4_dhcp", 4}, +{"ncd_net_ipv4_arp_probe", 4}, +{"ncd_net_watch_interfaces", 4}, +{"ncd_sys_watch_input", 4}, +{"ncd_sys_watch_usb", 4}, +{"ncd_sys_evdev", 4}, +{"ncd_sys_watch_directory", 4}, +{"StreamPeerIO", 4}, +{"DatagramPeerIO", 4}, +{"BReactor", 3}, +{"BSignal", 3}, +{"FragmentProtoAssembler", 4}, +{"BPredicate", 3}, +{"ServerConnection", 4}, +{"Listener", 4}, +{"DataProto", 4}, +{"FrameDecider", 4}, +{"BSocksClient", 4}, +{"BDHCPClientCore", 4}, +{"BDHCPClient", 4}, +{"NCDIfConfig", 4}, +{"BUnixSignal", 4}, +{"BProcess", 4}, +{"PRStreamSink", 4}, +{"PRStreamSource", 4}, +{"PacketProtoDecoder", 4}, +{"DPRelay", 4}, +{"BThreadWork", 4}, +{"DPReceive", 4}, +{"BInputProcess", 4}, +{"NCDUdevMonitorParser", 4}, +{"NCDUdevMonitor", 4}, +{"NCDUdevCache", 4}, +{"NCDUdevManager", 4}, +{"BTime", 4}, +{"BEncryption", 4}, +{"SPProtoDecoder", 4}, +{"LineBuffer", 4}, +{"BTap", 4}, +{"lwip", 4}, +{"NCDConfigTokenizer", 4}, +{"NCDConfigParser", 4}, +{"NCDValParser", 4}, +{"nsskey", 4}, +{"addr", 4}, +{"PasswordListener", 4}, +{"NCDInterfaceMonitor", 4}, +{"NCDRfkillMonitor", 4}, +{"udpgw", 4}, +{"UdpGwClient", 4}, +{"SocksUdpGwClient", 4}, +{"BNetwork", 4}, +{"BConnection", 4}, +{"BSSLConnection", 4}, +{"BDatagram", 4}, +{"PeerChat", 4}, +{"BArpProbe", 4}, +{"NCDModuleIndex", 4}, +{"NCDModuleProcess", 4}, +{"NCDValGenerator", 4}, +{"ncd_from_string", 4}, +{"ncd_to_string", 4}, +{"ncd_value", 4}, +{"ncd_try", 4}, +{"ncd_sys_request_server", 4}, +{"NCDRequest", 4}, +{"ncd_net_ipv6_wait_dynamic_addr", 4}, +{"NCDRequestClient", 4}, +{"ncd_request", 4}, +{"ncd_sys_request_client", 4}, +{"ncd_exit", 4}, +{"ncd_getargs", 4}, +{"ncd_arithmetic", 4}, +{"ncd_parse", 4}, +{"ncd_valuemetic", 4}, +{"ncd_file", 4}, +{"ncd_netmask", 4}, +{"ncd_implode", 4}, +{"ncd_call2", 4}, +{"ncd_assert", 4}, +{"ncd_reboot", 4}, +{"ncd_explode", 4}, +{"NCDPlaceholderDb", 4}, +{"NCDVal", 4}, +{"ncd_net_ipv6_addr", 4}, +{"ncd_net_ipv6_route", 4}, +{"ncd_net_ipv4_addr_in_network", 4}, +{"ncd_net_ipv6_addr_in_network", 4}, +{"dostest_server", 4}, +{"dostest_attacker", 4}, +{"ncd_timer", 4}, +{"ncd_file_open", 4}, +{"ncd_backtrack", 4}, +{"ncd_socket", 4}, +{"ncd_depend_scope", 4}, +{"ncd_substr", 4}, +{"ncd_sys_start_process", 4}, +{"NCDBuildProgram", 4}, +{"ncd_log", 4}, +{"ncd_log_msg", 4}, +{"ncd_buffer", 4}, +{"ncd_getenv", 4}, +{"BThreadSignal", 4}, +{"BLockReactor", 4}, +{"ncd_load_module", 4}, diff --git a/app/src/main/jni/badvpn/generated/bproto_addr.h b/app/src/main/jni/badvpn/generated/bproto_addr.h new file mode 100644 index 0000000..fbd96a8 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/bproto_addr.h @@ -0,0 +1,675 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define addr_SIZEtype (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint8_s)) +#define addr_SIZEip_port (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (2)) +#define addr_SIZEipv4_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (4)) +#define addr_SIZEipv6_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (16)) + +typedef struct { + uint8_t *out; + int used; + int type_count; + int ip_port_count; + int ipv4_addr_count; + int ipv6_addr_count; +} addrWriter; + +static void addrWriter_Init (addrWriter *o, uint8_t *out); +static int addrWriter_Finish (addrWriter *o); +static void addrWriter_Addtype (addrWriter *o, uint8_t v); +static uint8_t * addrWriter_Addip_port (addrWriter *o); +static uint8_t * addrWriter_Addipv4_addr (addrWriter *o); +static uint8_t * addrWriter_Addipv6_addr (addrWriter *o); + +typedef struct { + uint8_t *buf; + int buf_len; + int type_start; + int type_span; + int type_pos; + int ip_port_start; + int ip_port_span; + int ip_port_pos; + int ipv4_addr_start; + int ipv4_addr_span; + int ipv4_addr_pos; + int ipv6_addr_start; + int ipv6_addr_span; + int ipv6_addr_pos; +} addrParser; + +static int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len); +static int addrParser_GotEverything (addrParser *o); +static int addrParser_Gettype (addrParser *o, uint8_t *v); +static void addrParser_Resettype (addrParser *o); +static void addrParser_Forwardtype (addrParser *o); +static int addrParser_Getip_port (addrParser *o, uint8_t **data); +static void addrParser_Resetip_port (addrParser *o); +static void addrParser_Forwardip_port (addrParser *o); +static int addrParser_Getipv4_addr (addrParser *o, uint8_t **data); +static void addrParser_Resetipv4_addr (addrParser *o); +static void addrParser_Forwardipv4_addr (addrParser *o); +static int addrParser_Getipv6_addr (addrParser *o, uint8_t **data); +static void addrParser_Resetipv6_addr (addrParser *o); +static void addrParser_Forwardipv6_addr (addrParser *o); + +void addrWriter_Init (addrWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->type_count = 0; + o->ip_port_count = 0; + o->ipv4_addr_count = 0; + o->ipv6_addr_count = 0; +} + +int addrWriter_Finish (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 1) + ASSERT(o->ip_port_count >= 0 && o->ip_port_count <= 1) + ASSERT(o->ipv4_addr_count >= 0 && o->ipv4_addr_count <= 1) + ASSERT(o->ipv6_addr_count >= 0 && o->ipv6_addr_count <= 1) + + return o->used; +} + +void addrWriter_Addtype (addrWriter *o, uint8_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT8); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint8_s data; + data.v = htol8(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint8_s); + + o->type_count++; +} + +uint8_t * addrWriter_Addip_port (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ip_port_count == 0) + + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(2); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (2); + + o->ip_port_count++; + + return dest; +} + +uint8_t * addrWriter_Addipv4_addr (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ipv4_addr_count == 0) + + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(4); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (4); + + o->ipv4_addr_count++; + + return dest; +} + +uint8_t * addrWriter_Addipv6_addr (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ipv6_addr_count == 0) + + + struct BProto_header_s header; + header.id = htol16(4); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(16); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (16); + + o->ipv6_addr_count++; + + return dest; +} + +int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->type_start = o->buf_len; + o->type_span = 0; + o->type_pos = 0; + o->ip_port_start = o->buf_len; + o->ip_port_span = 0; + o->ip_port_pos = 0; + o->ipv4_addr_start = o->buf_len; + o->ipv4_addr_span = 0; + o->ipv4_addr_pos = 0; + o->ipv6_addr_start = o->buf_len; + o->ipv6_addr_span = 0; + o->ipv6_addr_pos = 0; + + int type_count = 0; + int ip_port_count = 0; + int ipv4_addr_count = 0; + int ipv6_addr_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + case 1: + if (o->type_start == o->buf_len) { + o->type_start = entry_pos; + } + o->type_span = pos - o->type_start; + type_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (2))) { + return 0; + } + if (o->ip_port_start == o->buf_len) { + o->ip_port_start = entry_pos; + } + o->ip_port_span = pos - o->ip_port_start; + ip_port_count++; + break; + case 3: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (4))) { + return 0; + } + if (o->ipv4_addr_start == o->buf_len) { + o->ipv4_addr_start = entry_pos; + } + o->ipv4_addr_span = pos - o->ipv4_addr_start; + ipv4_addr_count++; + break; + case 4: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (16))) { + return 0; + } + if (o->ipv6_addr_start == o->buf_len) { + o->ipv6_addr_start = entry_pos; + } + o->ipv6_addr_span = pos - o->ipv6_addr_start; + ipv6_addr_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(type_count == 1)) { + return 0; + } + if (!(ip_port_count <= 1)) { + return 0; + } + if (!(ipv4_addr_count <= 1)) { + return 0; + } + if (!(ipv6_addr_count <= 1)) { + return 0; + } + + return 1; +} + +int addrParser_GotEverything (addrParser *o) +{ + return ( + o->type_pos == o->type_span + && + o->ip_port_pos == o->ip_port_span + && + o->ipv4_addr_pos == o->ipv4_addr_span + && + o->ipv6_addr_pos == o->ipv6_addr_span + ); +} + +int addrParser_Gettype (addrParser *o, uint8_t *v) +{ + ASSERT(o->type_pos >= 0) + ASSERT(o->type_pos <= o->type_span) + + int left = o->type_span - o->type_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->type_start + o->type_pos, sizeof(header)); + o->type_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + struct BProto_uint8_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + if (id == 1) { + *v = ltoh8(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->type_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->type_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->type_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->type_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resettype (addrParser *o) +{ + o->type_pos = 0; +} + +void addrParser_Forwardtype (addrParser *o) +{ + o->type_pos = o->type_span; +} + +int addrParser_Getip_port (addrParser *o, uint8_t **data) +{ + ASSERT(o->ip_port_pos >= 0) + ASSERT(o->ip_port_pos <= o->ip_port_span) + + int left = o->ip_port_span - o->ip_port_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(header)); + o->ip_port_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ip_port_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ip_port_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ip_port_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ip_port_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(val)); + o->ip_port_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ip_port_start + o->ip_port_pos; + o->ip_port_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 2) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetip_port (addrParser *o) +{ + o->ip_port_pos = 0; +} + +void addrParser_Forwardip_port (addrParser *o) +{ + o->ip_port_pos = o->ip_port_span; +} + +int addrParser_Getipv4_addr (addrParser *o, uint8_t **data) +{ + ASSERT(o->ipv4_addr_pos >= 0) + ASSERT(o->ipv4_addr_pos <= o->ipv4_addr_span) + + int left = o->ipv4_addr_span - o->ipv4_addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(header)); + o->ipv4_addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(val)); + o->ipv4_addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ipv4_addr_start + o->ipv4_addr_pos; + o->ipv4_addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 3) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetipv4_addr (addrParser *o) +{ + o->ipv4_addr_pos = 0; +} + +void addrParser_Forwardipv4_addr (addrParser *o) +{ + o->ipv4_addr_pos = o->ipv4_addr_span; +} + +int addrParser_Getipv6_addr (addrParser *o, uint8_t **data) +{ + ASSERT(o->ipv6_addr_pos >= 0) + ASSERT(o->ipv6_addr_pos <= o->ipv6_addr_span) + + int left = o->ipv6_addr_span - o->ipv6_addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(header)); + o->ipv6_addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(val)); + o->ipv6_addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ipv6_addr_start + o->ipv6_addr_pos; + o->ipv6_addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 4) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetipv6_addr (addrParser *o) +{ + o->ipv6_addr_pos = 0; +} + +void addrParser_Forwardipv6_addr (addrParser *o) +{ + o->ipv6_addr_pos = o->ipv6_addr_span; +} + diff --git a/app/src/main/jni/badvpn/generated/bproto_bproto_test.h b/app/src/main/jni/badvpn/generated/bproto_bproto_test.h new file mode 100644 index 0000000..dc4ad80 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/bproto_bproto_test.h @@ -0,0 +1,1029 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define msg1_SIZEa (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg1_SIZEb (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint32_s)) +#define msg1_SIZEc (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint64_s)) +#define msg1_SIZEd (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg1_SIZEe (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint8_s)) +#define msg1_SIZEf(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg1_SIZEg (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (4)) + +typedef struct { + uint8_t *out; + int used; + int a_count; + int b_count; + int c_count; + int d_count; + int e_count; + int f_count; + int g_count; +} msg1Writer; + +static void msg1Writer_Init (msg1Writer *o, uint8_t *out); +static int msg1Writer_Finish (msg1Writer *o); +static void msg1Writer_Adda (msg1Writer *o, uint16_t v); +static void msg1Writer_Addb (msg1Writer *o, uint32_t v); +static void msg1Writer_Addc (msg1Writer *o, uint64_t v); +static void msg1Writer_Addd (msg1Writer *o, uint16_t v); +static void msg1Writer_Adde (msg1Writer *o, uint8_t v); +static uint8_t * msg1Writer_Addf (msg1Writer *o, int len); +static uint8_t * msg1Writer_Addg (msg1Writer *o); + +typedef struct { + uint8_t *buf; + int buf_len; + int a_start; + int a_span; + int a_pos; + int b_start; + int b_span; + int b_pos; + int c_start; + int c_span; + int c_pos; + int d_start; + int d_span; + int d_pos; + int e_start; + int e_span; + int e_pos; + int f_start; + int f_span; + int f_pos; + int g_start; + int g_span; + int g_pos; +} msg1Parser; + +static int msg1Parser_Init (msg1Parser *o, uint8_t *buf, int buf_len); +static int msg1Parser_GotEverything (msg1Parser *o); +static int msg1Parser_Geta (msg1Parser *o, uint16_t *v); +static void msg1Parser_Reseta (msg1Parser *o); +static void msg1Parser_Forwarda (msg1Parser *o); +static int msg1Parser_Getb (msg1Parser *o, uint32_t *v); +static void msg1Parser_Resetb (msg1Parser *o); +static void msg1Parser_Forwardb (msg1Parser *o); +static int msg1Parser_Getc (msg1Parser *o, uint64_t *v); +static void msg1Parser_Resetc (msg1Parser *o); +static void msg1Parser_Forwardc (msg1Parser *o); +static int msg1Parser_Getd (msg1Parser *o, uint16_t *v); +static void msg1Parser_Resetd (msg1Parser *o); +static void msg1Parser_Forwardd (msg1Parser *o); +static int msg1Parser_Gete (msg1Parser *o, uint8_t *v); +static void msg1Parser_Resete (msg1Parser *o); +static void msg1Parser_Forwarde (msg1Parser *o); +static int msg1Parser_Getf (msg1Parser *o, uint8_t **data, int *data_len); +static void msg1Parser_Resetf (msg1Parser *o); +static void msg1Parser_Forwardf (msg1Parser *o); +static int msg1Parser_Getg (msg1Parser *o, uint8_t **data); +static void msg1Parser_Resetg (msg1Parser *o); +static void msg1Parser_Forwardg (msg1Parser *o); + +void msg1Writer_Init (msg1Writer *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->a_count = 0; + o->b_count = 0; + o->c_count = 0; + o->d_count = 0; + o->e_count = 0; + o->f_count = 0; + o->g_count = 0; +} + +int msg1Writer_Finish (msg1Writer *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->a_count == 1) + ASSERT(o->b_count >= 0 && o->b_count <= 1) + ASSERT(o->c_count >= 1) + ASSERT(o->d_count >= 0) + ASSERT(o->e_count == 1) + ASSERT(o->f_count == 1) + ASSERT(o->g_count == 1) + + return o->used; +} + +void msg1Writer_Adda (msg1Writer *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->a_count == 0) + + + struct BProto_header_s header; + header.id = htol16(5); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->a_count++; +} + +void msg1Writer_Addb (msg1Writer *o, uint32_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->b_count == 0) + + + struct BProto_header_s header; + header.id = htol16(6); + header.type = htol16(BPROTO_TYPE_UINT32); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint32_s data; + data.v = htol32(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint32_s); + + o->b_count++; +} + +void msg1Writer_Addc (msg1Writer *o, uint64_t v) +{ + ASSERT(o->used >= 0) + + + + struct BProto_header_s header; + header.id = htol16(7); + header.type = htol16(BPROTO_TYPE_UINT64); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint64_s data; + data.v = htol64(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint64_s); + + o->c_count++; +} + +void msg1Writer_Addd (msg1Writer *o, uint16_t v) +{ + ASSERT(o->used >= 0) + + + + struct BProto_header_s header; + header.id = htol16(8); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->d_count++; +} + +void msg1Writer_Adde (msg1Writer *o, uint8_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->e_count == 0) + + + struct BProto_header_s header; + header.id = htol16(9); + header.type = htol16(BPROTO_TYPE_UINT8); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint8_s data; + data.v = htol8(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint8_s); + + o->e_count++; +} + +uint8_t * msg1Writer_Addf (msg1Writer *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->f_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(10); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->f_count++; + + return dest; +} + +uint8_t * msg1Writer_Addg (msg1Writer *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->g_count == 0) + + + struct BProto_header_s header; + header.id = htol16(11); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(4); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (4); + + o->g_count++; + + return dest; +} + +int msg1Parser_Init (msg1Parser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->a_start = o->buf_len; + o->a_span = 0; + o->a_pos = 0; + o->b_start = o->buf_len; + o->b_span = 0; + o->b_pos = 0; + o->c_start = o->buf_len; + o->c_span = 0; + o->c_pos = 0; + o->d_start = o->buf_len; + o->d_span = 0; + o->d_pos = 0; + o->e_start = o->buf_len; + o->e_span = 0; + o->e_pos = 0; + o->f_start = o->buf_len; + o->f_span = 0; + o->f_pos = 0; + o->g_start = o->buf_len; + o->g_span = 0; + o->g_pos = 0; + + int a_count = 0; + int b_count = 0; + int c_count = 0; + int d_count = 0; + int e_count = 0; + int f_count = 0; + int g_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + case 9: + if (o->e_start == o->buf_len) { + o->e_start = entry_pos; + } + o->e_span = pos - o->e_start; + e_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 5: + if (o->a_start == o->buf_len) { + o->a_start = entry_pos; + } + o->a_span = pos - o->a_start; + a_count++; + break; + case 8: + if (o->d_start == o->buf_len) { + o->d_start = entry_pos; + } + o->d_span = pos - o->d_start; + d_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + case 6: + if (o->b_start == o->buf_len) { + o->b_start = entry_pos; + } + o->b_span = pos - o->b_start; + b_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + case 7: + if (o->c_start == o->buf_len) { + o->c_start = entry_pos; + } + o->c_span = pos - o->c_start; + c_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 10: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->f_start == o->buf_len) { + o->f_start = entry_pos; + } + o->f_span = pos - o->f_start; + f_count++; + break; + case 11: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (4))) { + return 0; + } + if (o->g_start == o->buf_len) { + o->g_start = entry_pos; + } + o->g_span = pos - o->g_start; + g_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(a_count == 1)) { + return 0; + } + if (!(b_count <= 1)) { + return 0; + } + if (!(c_count >= 1)) { + return 0; + } + if (!(e_count == 1)) { + return 0; + } + if (!(f_count == 1)) { + return 0; + } + if (!(g_count == 1)) { + return 0; + } + + return 1; +} + +int msg1Parser_GotEverything (msg1Parser *o) +{ + return ( + o->a_pos == o->a_span + && + o->b_pos == o->b_span + && + o->c_pos == o->c_span + && + o->d_pos == o->d_span + && + o->e_pos == o->e_span + && + o->f_pos == o->f_span + && + o->g_pos == o->g_span + ); +} + +int msg1Parser_Geta (msg1Parser *o, uint16_t *v) +{ + ASSERT(o->a_pos >= 0) + ASSERT(o->a_pos <= o->a_span) + + int left = o->a_span - o->a_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->a_start + o->a_pos, sizeof(header)); + o->a_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->a_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->a_start + o->a_pos, sizeof(val)); + o->a_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 5) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->a_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->a_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->a_start + o->a_pos, sizeof(val)); + o->a_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->a_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Reseta (msg1Parser *o) +{ + o->a_pos = 0; +} + +void msg1Parser_Forwarda (msg1Parser *o) +{ + o->a_pos = o->a_span; +} + +int msg1Parser_Getb (msg1Parser *o, uint32_t *v) +{ + ASSERT(o->b_pos >= 0) + ASSERT(o->b_pos <= o->b_span) + + int left = o->b_span - o->b_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->b_start + o->b_pos, sizeof(header)); + o->b_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->b_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->b_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + struct BProto_uint32_s val; + memcpy(&val, o->buf + o->b_start + o->b_pos, sizeof(val)); + o->b_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + if (id == 6) { + *v = ltoh32(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->b_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->b_start + o->b_pos, sizeof(val)); + o->b_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->b_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetb (msg1Parser *o) +{ + o->b_pos = 0; +} + +void msg1Parser_Forwardb (msg1Parser *o) +{ + o->b_pos = o->b_span; +} + +int msg1Parser_Getc (msg1Parser *o, uint64_t *v) +{ + ASSERT(o->c_pos >= 0) + ASSERT(o->c_pos <= o->c_span) + + int left = o->c_span - o->c_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->c_start + o->c_pos, sizeof(header)); + o->c_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->c_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->c_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->c_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + struct BProto_uint64_s val; + memcpy(&val, o->buf + o->c_start + o->c_pos, sizeof(val)); + o->c_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + if (id == 7) { + *v = ltoh64(val.v); + return 1; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->c_start + o->c_pos, sizeof(val)); + o->c_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->c_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetc (msg1Parser *o) +{ + o->c_pos = 0; +} + +void msg1Parser_Forwardc (msg1Parser *o) +{ + o->c_pos = o->c_span; +} + +int msg1Parser_Getd (msg1Parser *o, uint16_t *v) +{ + ASSERT(o->d_pos >= 0) + ASSERT(o->d_pos <= o->d_span) + + int left = o->d_span - o->d_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->d_start + o->d_pos, sizeof(header)); + o->d_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->d_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->d_start + o->d_pos, sizeof(val)); + o->d_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 8) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->d_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->d_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->d_start + o->d_pos, sizeof(val)); + o->d_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->d_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetd (msg1Parser *o) +{ + o->d_pos = 0; +} + +void msg1Parser_Forwardd (msg1Parser *o) +{ + o->d_pos = o->d_span; +} + +int msg1Parser_Gete (msg1Parser *o, uint8_t *v) +{ + ASSERT(o->e_pos >= 0) + ASSERT(o->e_pos <= o->e_span) + + int left = o->e_span - o->e_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->e_start + o->e_pos, sizeof(header)); + o->e_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + struct BProto_uint8_s val; + memcpy(&val, o->buf + o->e_start + o->e_pos, sizeof(val)); + o->e_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + if (id == 9) { + *v = ltoh8(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->e_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->e_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->e_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->e_start + o->e_pos, sizeof(val)); + o->e_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->e_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resete (msg1Parser *o) +{ + o->e_pos = 0; +} + +void msg1Parser_Forwarde (msg1Parser *o) +{ + o->e_pos = o->e_span; +} + +int msg1Parser_Getf (msg1Parser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->f_pos >= 0) + ASSERT(o->f_pos <= o->f_span) + + int left = o->f_span - o->f_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->f_start + o->f_pos, sizeof(header)); + o->f_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->f_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->f_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->f_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->f_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->f_start + o->f_pos, sizeof(val)); + o->f_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->f_start + o->f_pos; + o->f_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 10) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetf (msg1Parser *o) +{ + o->f_pos = 0; +} + +void msg1Parser_Forwardf (msg1Parser *o) +{ + o->f_pos = o->f_span; +} + +int msg1Parser_Getg (msg1Parser *o, uint8_t **data) +{ + ASSERT(o->g_pos >= 0) + ASSERT(o->g_pos <= o->g_span) + + int left = o->g_span - o->g_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->g_start + o->g_pos, sizeof(header)); + o->g_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->g_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->g_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->g_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->g_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->g_start + o->g_pos, sizeof(val)); + o->g_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->g_start + o->g_pos; + o->g_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 11) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetg (msg1Parser *o) +{ + o->g_pos = 0; +} + +void msg1Parser_Forwardg (msg1Parser *o) +{ + o->g_pos = o->g_span; +} + diff --git a/app/src/main/jni/badvpn/generated/bproto_msgproto.h b/app/src/main/jni/badvpn/generated/bproto_msgproto.h new file mode 100644 index 0000000..e429223 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/bproto_msgproto.h @@ -0,0 +1,2122 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define msg_SIZEtype (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg_SIZEpayload(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int type_count; + int payload_count; +} msgWriter; + +static void msgWriter_Init (msgWriter *o, uint8_t *out); +static int msgWriter_Finish (msgWriter *o); +static void msgWriter_Addtype (msgWriter *o, uint16_t v); +static uint8_t * msgWriter_Addpayload (msgWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int type_start; + int type_span; + int type_pos; + int payload_start; + int payload_span; + int payload_pos; +} msgParser; + +static int msgParser_Init (msgParser *o, uint8_t *buf, int buf_len); +static int msgParser_GotEverything (msgParser *o); +static int msgParser_Gettype (msgParser *o, uint16_t *v); +static void msgParser_Resettype (msgParser *o); +static void msgParser_Forwardtype (msgParser *o); +static int msgParser_Getpayload (msgParser *o, uint8_t **data, int *data_len); +static void msgParser_Resetpayload (msgParser *o); +static void msgParser_Forwardpayload (msgParser *o); + +void msgWriter_Init (msgWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->type_count = 0; + o->payload_count = 0; +} + +int msgWriter_Finish (msgWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 1) + ASSERT(o->payload_count == 1) + + return o->used; +} + +void msgWriter_Addtype (msgWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->type_count++; +} + +uint8_t * msgWriter_Addpayload (msgWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->payload_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->payload_count++; + + return dest; +} + +int msgParser_Init (msgParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->type_start = o->buf_len; + o->type_span = 0; + o->type_pos = 0; + o->payload_start = o->buf_len; + o->payload_span = 0; + o->payload_pos = 0; + + int type_count = 0; + int payload_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->type_start == o->buf_len) { + o->type_start = entry_pos; + } + o->type_span = pos - o->type_start; + type_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->payload_start == o->buf_len) { + o->payload_start = entry_pos; + } + o->payload_span = pos - o->payload_start; + payload_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(type_count == 1)) { + return 0; + } + if (!(payload_count == 1)) { + return 0; + } + + return 1; +} + +int msgParser_GotEverything (msgParser *o) +{ + return ( + o->type_pos == o->type_span + && + o->payload_pos == o->payload_span + ); +} + +int msgParser_Gettype (msgParser *o, uint16_t *v) +{ + ASSERT(o->type_pos >= 0) + ASSERT(o->type_pos <= o->type_span) + + int left = o->type_span - o->type_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->type_start + o->type_pos, sizeof(header)); + o->type_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->type_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->type_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->type_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->type_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msgParser_Resettype (msgParser *o) +{ + o->type_pos = 0; +} + +void msgParser_Forwardtype (msgParser *o) +{ + o->type_pos = o->type_span; +} + +int msgParser_Getpayload (msgParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->payload_pos >= 0) + ASSERT(o->payload_pos <= o->payload_span) + + int left = o->payload_span - o->payload_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->payload_start + o->payload_pos, sizeof(header)); + o->payload_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->payload_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->payload_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->payload_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->payload_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->payload_start + o->payload_pos, sizeof(val)); + o->payload_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->payload_start + o->payload_pos; + o->payload_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msgParser_Resetpayload (msgParser *o) +{ + o->payload_pos = 0; +} + +void msgParser_Forwardpayload (msgParser *o) +{ + o->payload_pos = o->payload_span; +} + +#define msg_youconnect_SIZEaddr(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_SIZEkey(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_SIZEpassword (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint64_s)) + +typedef struct { + uint8_t *out; + int used; + int addr_count; + int key_count; + int password_count; +} msg_youconnectWriter; + +static void msg_youconnectWriter_Init (msg_youconnectWriter *o, uint8_t *out); +static int msg_youconnectWriter_Finish (msg_youconnectWriter *o); +static uint8_t * msg_youconnectWriter_Addaddr (msg_youconnectWriter *o, int len); +static uint8_t * msg_youconnectWriter_Addkey (msg_youconnectWriter *o, int len); +static void msg_youconnectWriter_Addpassword (msg_youconnectWriter *o, uint64_t v); + +typedef struct { + uint8_t *buf; + int buf_len; + int addr_start; + int addr_span; + int addr_pos; + int key_start; + int key_span; + int key_pos; + int password_start; + int password_span; + int password_pos; +} msg_youconnectParser; + +static int msg_youconnectParser_Init (msg_youconnectParser *o, uint8_t *buf, int buf_len); +static int msg_youconnectParser_GotEverything (msg_youconnectParser *o); +static int msg_youconnectParser_Getaddr (msg_youconnectParser *o, uint8_t **data, int *data_len); +static void msg_youconnectParser_Resetaddr (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardaddr (msg_youconnectParser *o); +static int msg_youconnectParser_Getkey (msg_youconnectParser *o, uint8_t **data, int *data_len); +static void msg_youconnectParser_Resetkey (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardkey (msg_youconnectParser *o); +static int msg_youconnectParser_Getpassword (msg_youconnectParser *o, uint64_t *v); +static void msg_youconnectParser_Resetpassword (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardpassword (msg_youconnectParser *o); + +void msg_youconnectWriter_Init (msg_youconnectWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->addr_count = 0; + o->key_count = 0; + o->password_count = 0; +} + +int msg_youconnectWriter_Finish (msg_youconnectWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->addr_count >= 1) + ASSERT(o->key_count >= 0 && o->key_count <= 1) + ASSERT(o->password_count >= 0 && o->password_count <= 1) + + return o->used; +} + +uint8_t * msg_youconnectWriter_Addaddr (msg_youconnectWriter *o, int len) +{ + ASSERT(o->used >= 0) + + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->addr_count++; + + return dest; +} + +uint8_t * msg_youconnectWriter_Addkey (msg_youconnectWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->key_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->key_count++; + + return dest; +} + +void msg_youconnectWriter_Addpassword (msg_youconnectWriter *o, uint64_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->password_count == 0) + + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_UINT64); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint64_s data; + data.v = htol64(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint64_s); + + o->password_count++; +} + +int msg_youconnectParser_Init (msg_youconnectParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->addr_start = o->buf_len; + o->addr_span = 0; + o->addr_pos = 0; + o->key_start = o->buf_len; + o->key_span = 0; + o->key_pos = 0; + o->password_start = o->buf_len; + o->password_span = 0; + o->password_pos = 0; + + int addr_count = 0; + int key_count = 0; + int password_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + case 3: + if (o->password_start == o->buf_len) { + o->password_start = entry_pos; + } + o->password_span = pos - o->password_start; + password_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 1: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->addr_start == o->buf_len) { + o->addr_start = entry_pos; + } + o->addr_span = pos - o->addr_start; + addr_count++; + break; + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->key_start == o->buf_len) { + o->key_start = entry_pos; + } + o->key_span = pos - o->key_start; + key_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(addr_count >= 1)) { + return 0; + } + if (!(key_count <= 1)) { + return 0; + } + if (!(password_count <= 1)) { + return 0; + } + + return 1; +} + +int msg_youconnectParser_GotEverything (msg_youconnectParser *o) +{ + return ( + o->addr_pos == o->addr_span + && + o->key_pos == o->key_span + && + o->password_pos == o->password_span + ); +} + +int msg_youconnectParser_Getaddr (msg_youconnectParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->addr_pos >= 0) + ASSERT(o->addr_pos <= o->addr_span) + + int left = o->addr_span - o->addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->addr_start + o->addr_pos, sizeof(header)); + o->addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->addr_start + o->addr_pos, sizeof(val)); + o->addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->addr_start + o->addr_pos; + o->addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 1) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetaddr (msg_youconnectParser *o) +{ + o->addr_pos = 0; +} + +void msg_youconnectParser_Forwardaddr (msg_youconnectParser *o) +{ + o->addr_pos = o->addr_span; +} + +int msg_youconnectParser_Getkey (msg_youconnectParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->key_pos >= 0) + ASSERT(o->key_pos <= o->key_span) + + int left = o->key_span - o->key_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->key_start + o->key_pos, sizeof(header)); + o->key_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->key_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->key_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->key_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->key_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->key_start + o->key_pos, sizeof(val)); + o->key_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->key_start + o->key_pos; + o->key_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetkey (msg_youconnectParser *o) +{ + o->key_pos = 0; +} + +void msg_youconnectParser_Forwardkey (msg_youconnectParser *o) +{ + o->key_pos = o->key_span; +} + +int msg_youconnectParser_Getpassword (msg_youconnectParser *o, uint64_t *v) +{ + ASSERT(o->password_pos >= 0) + ASSERT(o->password_pos <= o->password_span) + + int left = o->password_span - o->password_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->password_start + o->password_pos, sizeof(header)); + o->password_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->password_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->password_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->password_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + struct BProto_uint64_s val; + memcpy(&val, o->buf + o->password_start + o->password_pos, sizeof(val)); + o->password_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + if (id == 3) { + *v = ltoh64(val.v); + return 1; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->password_start + o->password_pos, sizeof(val)); + o->password_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->password_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetpassword (msg_youconnectParser *o) +{ + o->password_pos = 0; +} + +void msg_youconnectParser_Forwardpassword (msg_youconnectParser *o) +{ + o->password_pos = o->password_span; +} + +#define msg_youconnect_addr_SIZEname(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_addr_SIZEaddr(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int name_count; + int addr_count; +} msg_youconnect_addrWriter; + +static void msg_youconnect_addrWriter_Init (msg_youconnect_addrWriter *o, uint8_t *out); +static int msg_youconnect_addrWriter_Finish (msg_youconnect_addrWriter *o); +static uint8_t * msg_youconnect_addrWriter_Addname (msg_youconnect_addrWriter *o, int len); +static uint8_t * msg_youconnect_addrWriter_Addaddr (msg_youconnect_addrWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int name_start; + int name_span; + int name_pos; + int addr_start; + int addr_span; + int addr_pos; +} msg_youconnect_addrParser; + +static int msg_youconnect_addrParser_Init (msg_youconnect_addrParser *o, uint8_t *buf, int buf_len); +static int msg_youconnect_addrParser_GotEverything (msg_youconnect_addrParser *o); +static int msg_youconnect_addrParser_Getname (msg_youconnect_addrParser *o, uint8_t **data, int *data_len); +static void msg_youconnect_addrParser_Resetname (msg_youconnect_addrParser *o); +static void msg_youconnect_addrParser_Forwardname (msg_youconnect_addrParser *o); +static int msg_youconnect_addrParser_Getaddr (msg_youconnect_addrParser *o, uint8_t **data, int *data_len); +static void msg_youconnect_addrParser_Resetaddr (msg_youconnect_addrParser *o); +static void msg_youconnect_addrParser_Forwardaddr (msg_youconnect_addrParser *o); + +void msg_youconnect_addrWriter_Init (msg_youconnect_addrWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->name_count = 0; + o->addr_count = 0; +} + +int msg_youconnect_addrWriter_Finish (msg_youconnect_addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->name_count == 1) + ASSERT(o->addr_count == 1) + + return o->used; +} + +uint8_t * msg_youconnect_addrWriter_Addname (msg_youconnect_addrWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->name_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->name_count++; + + return dest; +} + +uint8_t * msg_youconnect_addrWriter_Addaddr (msg_youconnect_addrWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->addr_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->addr_count++; + + return dest; +} + +int msg_youconnect_addrParser_Init (msg_youconnect_addrParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->name_start = o->buf_len; + o->name_span = 0; + o->name_pos = 0; + o->addr_start = o->buf_len; + o->addr_span = 0; + o->addr_pos = 0; + + int name_count = 0; + int addr_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 1: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->name_start == o->buf_len) { + o->name_start = entry_pos; + } + o->name_span = pos - o->name_start; + name_count++; + break; + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->addr_start == o->buf_len) { + o->addr_start = entry_pos; + } + o->addr_span = pos - o->addr_start; + addr_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(name_count == 1)) { + return 0; + } + if (!(addr_count == 1)) { + return 0; + } + + return 1; +} + +int msg_youconnect_addrParser_GotEverything (msg_youconnect_addrParser *o) +{ + return ( + o->name_pos == o->name_span + && + o->addr_pos == o->addr_span + ); +} + +int msg_youconnect_addrParser_Getname (msg_youconnect_addrParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->name_pos >= 0) + ASSERT(o->name_pos <= o->name_span) + + int left = o->name_span - o->name_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->name_start + o->name_pos, sizeof(header)); + o->name_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->name_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->name_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->name_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->name_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->name_start + o->name_pos, sizeof(val)); + o->name_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->name_start + o->name_pos; + o->name_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 1) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnect_addrParser_Resetname (msg_youconnect_addrParser *o) +{ + o->name_pos = 0; +} + +void msg_youconnect_addrParser_Forwardname (msg_youconnect_addrParser *o) +{ + o->name_pos = o->name_span; +} + +int msg_youconnect_addrParser_Getaddr (msg_youconnect_addrParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->addr_pos >= 0) + ASSERT(o->addr_pos <= o->addr_span) + + int left = o->addr_span - o->addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->addr_start + o->addr_pos, sizeof(header)); + o->addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->addr_start + o->addr_pos, sizeof(val)); + o->addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->addr_start + o->addr_pos; + o->addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnect_addrParser_Resetaddr (msg_youconnect_addrParser *o) +{ + o->addr_pos = 0; +} + +void msg_youconnect_addrParser_Forwardaddr (msg_youconnect_addrParser *o) +{ + o->addr_pos = o->addr_span; +} + +#define msg_seed_SIZEseed_id (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg_seed_SIZEkey(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_seed_SIZEiv(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int seed_id_count; + int key_count; + int iv_count; +} msg_seedWriter; + +static void msg_seedWriter_Init (msg_seedWriter *o, uint8_t *out); +static int msg_seedWriter_Finish (msg_seedWriter *o); +static void msg_seedWriter_Addseed_id (msg_seedWriter *o, uint16_t v); +static uint8_t * msg_seedWriter_Addkey (msg_seedWriter *o, int len); +static uint8_t * msg_seedWriter_Addiv (msg_seedWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int seed_id_start; + int seed_id_span; + int seed_id_pos; + int key_start; + int key_span; + int key_pos; + int iv_start; + int iv_span; + int iv_pos; +} msg_seedParser; + +static int msg_seedParser_Init (msg_seedParser *o, uint8_t *buf, int buf_len); +static int msg_seedParser_GotEverything (msg_seedParser *o); +static int msg_seedParser_Getseed_id (msg_seedParser *o, uint16_t *v); +static void msg_seedParser_Resetseed_id (msg_seedParser *o); +static void msg_seedParser_Forwardseed_id (msg_seedParser *o); +static int msg_seedParser_Getkey (msg_seedParser *o, uint8_t **data, int *data_len); +static void msg_seedParser_Resetkey (msg_seedParser *o); +static void msg_seedParser_Forwardkey (msg_seedParser *o); +static int msg_seedParser_Getiv (msg_seedParser *o, uint8_t **data, int *data_len); +static void msg_seedParser_Resetiv (msg_seedParser *o); +static void msg_seedParser_Forwardiv (msg_seedParser *o); + +void msg_seedWriter_Init (msg_seedWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->seed_id_count = 0; + o->key_count = 0; + o->iv_count = 0; +} + +int msg_seedWriter_Finish (msg_seedWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 1) + ASSERT(o->key_count == 1) + ASSERT(o->iv_count == 1) + + return o->used; +} + +void msg_seedWriter_Addseed_id (msg_seedWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->seed_id_count++; +} + +uint8_t * msg_seedWriter_Addkey (msg_seedWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->key_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->key_count++; + + return dest; +} + +uint8_t * msg_seedWriter_Addiv (msg_seedWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->iv_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->iv_count++; + + return dest; +} + +int msg_seedParser_Init (msg_seedParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->seed_id_start = o->buf_len; + o->seed_id_span = 0; + o->seed_id_pos = 0; + o->key_start = o->buf_len; + o->key_span = 0; + o->key_pos = 0; + o->iv_start = o->buf_len; + o->iv_span = 0; + o->iv_pos = 0; + + int seed_id_count = 0; + int key_count = 0; + int iv_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->seed_id_start == o->buf_len) { + o->seed_id_start = entry_pos; + } + o->seed_id_span = pos - o->seed_id_start; + seed_id_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->key_start == o->buf_len) { + o->key_start = entry_pos; + } + o->key_span = pos - o->key_start; + key_count++; + break; + case 3: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->iv_start == o->buf_len) { + o->iv_start = entry_pos; + } + o->iv_span = pos - o->iv_start; + iv_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(seed_id_count == 1)) { + return 0; + } + if (!(key_count == 1)) { + return 0; + } + if (!(iv_count == 1)) { + return 0; + } + + return 1; +} + +int msg_seedParser_GotEverything (msg_seedParser *o) +{ + return ( + o->seed_id_pos == o->seed_id_span + && + o->key_pos == o->key_span + && + o->iv_pos == o->iv_span + ); +} + +int msg_seedParser_Getseed_id (msg_seedParser *o, uint16_t *v) +{ + ASSERT(o->seed_id_pos >= 0) + ASSERT(o->seed_id_pos <= o->seed_id_span) + + int left = o->seed_id_span - o->seed_id_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(header)); + o->seed_id_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->seed_id_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->seed_id_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->seed_id_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->seed_id_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetseed_id (msg_seedParser *o) +{ + o->seed_id_pos = 0; +} + +void msg_seedParser_Forwardseed_id (msg_seedParser *o) +{ + o->seed_id_pos = o->seed_id_span; +} + +int msg_seedParser_Getkey (msg_seedParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->key_pos >= 0) + ASSERT(o->key_pos <= o->key_span) + + int left = o->key_span - o->key_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->key_start + o->key_pos, sizeof(header)); + o->key_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->key_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->key_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->key_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->key_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->key_start + o->key_pos, sizeof(val)); + o->key_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->key_start + o->key_pos; + o->key_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetkey (msg_seedParser *o) +{ + o->key_pos = 0; +} + +void msg_seedParser_Forwardkey (msg_seedParser *o) +{ + o->key_pos = o->key_span; +} + +int msg_seedParser_Getiv (msg_seedParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->iv_pos >= 0) + ASSERT(o->iv_pos <= o->iv_span) + + int left = o->iv_span - o->iv_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->iv_start + o->iv_pos, sizeof(header)); + o->iv_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->iv_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->iv_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->iv_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->iv_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->iv_start + o->iv_pos, sizeof(val)); + o->iv_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->iv_start + o->iv_pos; + o->iv_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 3) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetiv (msg_seedParser *o) +{ + o->iv_pos = 0; +} + +void msg_seedParser_Forwardiv (msg_seedParser *o) +{ + o->iv_pos = o->iv_span; +} + +#define msg_confirmseed_SIZEseed_id (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) + +typedef struct { + uint8_t *out; + int used; + int seed_id_count; +} msg_confirmseedWriter; + +static void msg_confirmseedWriter_Init (msg_confirmseedWriter *o, uint8_t *out); +static int msg_confirmseedWriter_Finish (msg_confirmseedWriter *o); +static void msg_confirmseedWriter_Addseed_id (msg_confirmseedWriter *o, uint16_t v); + +typedef struct { + uint8_t *buf; + int buf_len; + int seed_id_start; + int seed_id_span; + int seed_id_pos; +} msg_confirmseedParser; + +static int msg_confirmseedParser_Init (msg_confirmseedParser *o, uint8_t *buf, int buf_len); +static int msg_confirmseedParser_GotEverything (msg_confirmseedParser *o); +static int msg_confirmseedParser_Getseed_id (msg_confirmseedParser *o, uint16_t *v); +static void msg_confirmseedParser_Resetseed_id (msg_confirmseedParser *o); +static void msg_confirmseedParser_Forwardseed_id (msg_confirmseedParser *o); + +void msg_confirmseedWriter_Init (msg_confirmseedWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->seed_id_count = 0; +} + +int msg_confirmseedWriter_Finish (msg_confirmseedWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 1) + + return o->used; +} + +void msg_confirmseedWriter_Addseed_id (msg_confirmseedWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->seed_id_count++; +} + +int msg_confirmseedParser_Init (msg_confirmseedParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->seed_id_start = o->buf_len; + o->seed_id_span = 0; + o->seed_id_pos = 0; + + int seed_id_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->seed_id_start == o->buf_len) { + o->seed_id_start = entry_pos; + } + o->seed_id_span = pos - o->seed_id_start; + seed_id_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(seed_id_count == 1)) { + return 0; + } + + return 1; +} + +int msg_confirmseedParser_GotEverything (msg_confirmseedParser *o) +{ + return ( + o->seed_id_pos == o->seed_id_span + ); +} + +int msg_confirmseedParser_Getseed_id (msg_confirmseedParser *o, uint16_t *v) +{ + ASSERT(o->seed_id_pos >= 0) + ASSERT(o->seed_id_pos <= o->seed_id_span) + + int left = o->seed_id_span - o->seed_id_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(header)); + o->seed_id_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->seed_id_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->seed_id_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->seed_id_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->seed_id_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_confirmseedParser_Resetseed_id (msg_confirmseedParser *o) +{ + o->seed_id_pos = 0; +} + +void msg_confirmseedParser_Forwardseed_id (msg_confirmseedParser *o) +{ + o->seed_id_pos = o->seed_id_span; +} + diff --git a/app/src/main/jni/badvpn/generated/flex_BPredicate.c b/app/src/main/jni/badvpn/generated/flex_BPredicate.c new file mode 100644 index 0000000..95f4ff6 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/flex_BPredicate.c @@ -0,0 +1,2143 @@ +#line 2 "generated//flex_BPredicate.c" + +#line 4 "generated//flex_BPredicate.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if 1 + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +static void yyensure_buffer_stack (yyscan_t yyscanner ); +static void yy_load_buffer_state (yyscan_t yyscanner ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 13 +#define YY_END_OF_BUFFER 14 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[34] = + { 0, + 0, 0, 14, 12, 11, 11, 12, 1, 2, 3, + 9, 9, 9, 9, 9, 9, 11, 0, 10, 9, + 9, 9, 5, 9, 9, 4, 6, 9, 9, 9, + 7, 8, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 5, + 6, 1, 1, 7, 1, 1, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 9, 8, 8, 10, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 11, 12, 8, + 8, 13, 8, 14, 8, 8, 8, 8, 8, 8, + 1, 1, 1, 1, 8, 1, 15, 8, 8, 8, + + 16, 17, 8, 8, 8, 8, 8, 18, 8, 8, + 8, 8, 8, 19, 20, 21, 22, 8, 8, 8, + 8, 8, 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, 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, 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, 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, 1 + } ; + +static yyconst flex_int32_t yy_meta[23] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2 + } ; + +static yyconst flex_int16_t yy_base[36] = + { 0, + 0, 0, 46, 47, 21, 23, 41, 47, 47, 47, + 0, 33, 31, 29, 26, 21, 25, 35, 47, 0, + 28, 23, 0, 18, 13, 0, 0, 14, 17, 16, + 0, 0, 47, 28, 29 + } ; + +static yyconst flex_int16_t yy_def[36] = + { 0, + 33, 1, 33, 33, 33, 33, 34, 33, 33, 33, + 35, 35, 35, 35, 35, 35, 33, 34, 33, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 0, 33, 33 + } ; + +static yyconst flex_int16_t yy_nxt[70] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, + 13, 14, 11, 11, 11, 11, 15, 11, 11, 11, + 16, 11, 17, 17, 17, 17, 17, 17, 18, 18, + 20, 32, 31, 30, 29, 28, 27, 26, 19, 25, + 24, 23, 22, 21, 19, 33, 3, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33 + } ; + +static yyconst flex_int16_t yy_chk[70] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 5, 6, 6, 17, 17, 34, 34, + 35, 30, 29, 28, 25, 24, 22, 21, 18, 16, + 15, 14, 13, 12, 7, 3, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "predicate/BPredicate.l" +/** + * @file BPredicate.l + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} lexer file. + */ +#line 35 "predicate/BPredicate.l" + +#include +#include + +#include +#include + +#include + +#define YY_INPUT(buffer, res, max_size) \ + int bytes_read = LexMemoryBufferInput_Read((LexMemoryBufferInput *)yyget_extra(yyscanner), buffer, max_size); \ + res = (bytes_read == 0 ? YY_NULL : bytes_read); + +#define YY_NO_UNISTD_H 1 +#line 503 "generated//flex_BPredicate.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + + static void yy_push_state (int new_state ,yyscan_t yyscanner); + + static void yy_pop_state (yyscan_t yyscanner ); + + static int yy_top_state (yyscan_t yyscanner ); + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 52 "predicate/BPredicate.l" + +#line 756 "generated//flex_BPredicate.c" + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 33 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 53 "predicate/BPredicate.l" +return SPAR; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 54 "predicate/BPredicate.l" +return EPAR; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 55 "predicate/BPredicate.l" +return COMMA; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 56 "predicate/BPredicate.l" +return AND; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 57 "predicate/BPredicate.l" +return OR; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 58 "predicate/BPredicate.l" +return NOT; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 59 "predicate/BPredicate.l" +return CONSTANT_TRUE; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 60 "predicate/BPredicate.l" +return CONSTANT_FALSE; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 61 "predicate/BPredicate.l" +{ + int l = strlen(yytext); + char *p = (char *)malloc(l + 1); + if (p) { + memcpy(p, yytext, l); + p[l] = '\0'; + } + yylval->text = p; + return NAME; + } + YY_BREAK +case 10: +/* rule 10 can match eol */ +YY_RULE_SETUP +#line 71 "predicate/BPredicate.l" +{ + int l = strlen(yytext); + char *p = (char *)malloc(l - 1); + if (p) { + memcpy(p, yytext + 1, l - 2); + p[l - 2] = '\0'; + } + yylval->text = p; + return STRING; + } + YY_BREAK +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 81 "predicate/BPredicate.l" +; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 82 "predicate/BPredicate.l" +LexMemoryBufferInput_SetError((LexMemoryBufferInput *)yyget_extra(yyscanner)); return 0; // remember failure and report EOF + YY_BREAK +case 13: +YY_RULE_SETUP +#line 83 "predicate/BPredicate.l" +ECHO; + YY_BREAK +#line 924 "generated//flex_BPredicate.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 33); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register yy_size_t number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + yy_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ,yyscanner ); + + yyfree((void *) b ,yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + + static void yy_push_state (int new_state , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth ) + { + yy_size_t new_size; + + yyg->yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yyg->yy_start_stack_depth * sizeof( int ); + + if ( ! yyg->yy_start_stack ) + yyg->yy_start_stack = (int *) yyalloc(new_size ,yyscanner ); + + else + yyg->yy_start_stack = (int *) yyrealloc((void *) yyg->yy_start_stack,new_size ,yyscanner ); + + if ( ! yyg->yy_start_stack ) + YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); + } + + yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); +} + + static void yy_pop_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( --yyg->yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]); +} + + static int yy_top_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyg->yy_start_stack[yyg->yy_start_stack_ptr - 1]; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void yyset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int yylex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ + +int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 83 "predicate/BPredicate.l" + + + diff --git a/app/src/main/jni/badvpn/generated/flex_BPredicate.h b/app/src/main/jni/badvpn/generated/flex_BPredicate.h new file mode 100644 index 0000000..f3d2428 --- /dev/null +++ b/app/src/main/jni/badvpn/generated/flex_BPredicate.h @@ -0,0 +1,350 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "generated//flex_BPredicate.h" + +#line 8 "generated//flex_BPredicate.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if 1 + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 83 "predicate/BPredicate.l" + + +#line 349 "generated//flex_BPredicate.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/app/src/main/jni/badvpn/lemon/lemon.c b/app/src/main/jni/badvpn/lemon/lemon.c new file mode 100644 index 0000000..8336e9a --- /dev/null +++ b/app/src/main/jni/badvpn/lemon/lemon.c @@ -0,0 +1,4889 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +#ifdef __WIN32__ +extern int access(); +#else +#include +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +static char *msort(char*,char**,int(*)(const char*,const char*)); + +/* +** Compilers are getting increasingly pedantic about type conversions +** as C evolves ever closer to Ada.... To work around the latest problems +** we have to define the following variant of strlen(). +*/ +#define lemonStrlen(X) ((int)strlen(X)) + +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + char *arg; + char *message; +}; +int OptInit(/* char**,struct s_options*,FILE* */); +int OptNArgs(/* void */); +char *OptArg(/* int */); +void OptErr(/* int */); +void OptPrint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); +void ResortStates(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL, + MULTITERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if precedence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destLineno; /* Line number for start of destructor */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + RRCONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int statenum; /* Sequential number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + struct symbol *wildcard; /* Token that matches anything */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + char *error; /* Code to execute when an error is seen */ + char *overflow; /* Code to execute on a stack overflow */ + char *failure; /* Code to execute on parser failure */ + char *accept; /* Code to execute when the parser excepts */ + char *extracode; /* Code appended to the generated file */ + char *tokendest; /* Code to execute to destroy token data */ + char *vardest; /* Code for the default non-terminal destructor */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammar */ + int nolinenosflag; /* True if #line statements should not be printed */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +static struct action *Action_new(void){ + static struct action *freelist = 0; + struct action *new; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)calloc(amt, sizeof(struct action)); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp( + struct action *ap1, + struct action *ap2 +){ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ){ + rc = (int)ap1->type - (int)ap2->type; + } + if( rc==0 && ap1->type==REDUCE ){ + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +static struct action *Action_sort( + struct action *ap +){ + ap = (struct action *)msort((char *)ap,(char **)&ap->next, + (int(*)(const char*,const char*))actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +char *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure +*/ +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ + } *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = calloc( 1, sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset where we can + ** insert the current transaction set. Fall out of the loop when that + ** offset is found. In the worst case, we fall out of the loop when + ** i reaches p->nAction, which means we append the new transaction set. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=0; inAction+p->mnLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + }else if( p->aAction[i].lookahead==p->mnLookahead ){ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* Same as a prior transaction set */ + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i, j; + for(i=0; inrhs && rp->precsym==0; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + if( sp->subsym[j]->prec>=0 ){ + rp->precsym = sp->subsym[j]; + break; + } + } + }else if( sp->prec>=0 ){ + rp->precsym = rp->rhs[i]; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i, j; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = LEMON_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type!=TERMINAL || sp->lambda==LEMON_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = LEMON_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s2->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + progress += SetAdd(s1->firstset,s2->subsym[j]->index); + } + break; + }else if( s1==s2 ){ + if( s1->lambda==LEMON_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==LEMON_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + rp->lhsStart = 1; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* +** Return true if two symbols are the same. +*/ +int same_symbol(a,b) +struct symbol *a; +struct symbol *b; +{ + int i; + if( a==b ) return 1; + if( a->type!=MULTITERMINAL ) return 0; + if( b->type!=MULTITERMINAL ) return 0; + if( a->nsubsym!=b->nsubsym ) return 0; + for(i=0; insubsym; i++){ + if( a->subsym[i]!=b->subsym[i] ) return 0; + } + return 1; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + if( sp->type==MULTITERMINAL ){ + int i; + for(i=0; insubsym; i++){ + Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); + } + }else{ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + /* assert( stp->ap ); */ + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolved, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==SHIFT ){ + apy->type = SSCONFLICT; + errcnt++; + } + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = SRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = SRCONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = RRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==SSCONFLICT || + apx->type==SRCONFLICT || + apx->type==RRCONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==SSCONFLICT || + apy->type==SRCONFLICT || + apy->type==RRCONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)calloc( amt, sizeof(struct config) ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else if( xsp->type==MULTITERMINAL ){ + int k; + for(k=0; knsubsym; k++){ + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==LEMON_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = lemonStrlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = lemonStrlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = malloc( lemonStrlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + + +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static int nolinenosflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, + {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + memset(&lem, 0, sizeof(lem)); + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.nolinenosflag = nolinenosflag; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + lem.errsym->useCnt = 0; + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.nrule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal+1); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Reorder and renumber the states so that states with fewer choices + ** occur at the end. */ + ResortStates(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); + return (lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((unsigned long)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge( + char *a, + char *b, + int (*cmp)(const char*,const char*), + int offset +){ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +static char *msort( + char *list, + char **next, + int (*cmp)(const char*,const char*) +){ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is no prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)calloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbols on RHS of rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){ + struct symbol *msp = psp->rhs[psp->nrhs-1]; + if( msp->type!=MULTITERMINAL ){ + struct symbol *origsp = msp; + msp = calloc(1,sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = calloc(1,sizeof(struct symbol*)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs-1] = msp; + } + msp->nsubsym++; + msp->subsym = realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym); + msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); + if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllinenoslot = 0; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + psp->insertLineMacro = 0; + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + psp->insertLineMacro = 0; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + psp->insertLineMacro = 0; + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + psp->insertLineMacro = 0; + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + psp->insertLineMacro = 0; + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else if( strcmp(x,"wildcard")==0 ){ + psp->state = WAITING_FOR_WILDCARD_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->insertLineMacro = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ + char *zOld, *zNew, *zBuf, *z; + int nOld, n, nLine, nNew, nBack; + int addLineMacro; + char zLine[50]; + zNew = x; + if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; + nNew = lemonStrlen(zNew); + if( *psp->declargslot ){ + zOld = *psp->declargslot; + }else{ + zOld = ""; + } + nOld = lemonStrlen(zOld); + n = nOld + nNew + 20; + addLineMacro = !psp->gp->nolinenosflag && psp->insertLineMacro && + (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0); + if( addLineMacro ){ + for(z=psp->filename, nBack=0; *z; z++){ + if( *z=='\\' ) nBack++; + } + sprintf(zLine, "#line %d ", psp->tokenlineno); + nLine = lemonStrlen(zLine); + n += nLine + lemonStrlen(psp->filename) + nBack; + } + *psp->declargslot = zBuf = realloc(*psp->declargslot, n); + zBuf += nOld; + if( addLineMacro ){ + if( nOld && zBuf[-1]!='\n' ){ + *(zBuf++) = '\n'; + } + memcpy(zBuf, zLine, nLine); + zBuf += nLine; + *(zBuf++) = '"'; + for(z=psp->filename; *z; z++){ + if( *z=='\\' ){ + *(zBuf++) = '\\'; + } + *(zBuf++) = *z; + } + *(zBuf++) = '"'; + *(zBuf++) = '\n'; + } + if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){ + psp->decllinenoslot[0] = psp->tokenlineno; + } + memcpy(zBuf, zNew, nNew); + zBuf += nNew; + *zBuf = 0; + psp->state = WAITING_FOR_DECL_OR_RULE; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case WAITING_FOR_WILDCARD_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%wildcard argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->gp->wildcard==0 ){ + psp->gp->wildcard = sp; + }else{ + ErrorMsg(psp->filename, psp->tokenlineno, + "Extra wildcard to token: %s", x); + psp->errorcnt++; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the preprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static void preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start = 0; + int lineno = 1; + int start_lineno = 1; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){ + cp += 2; + while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( lemonStrlen(lemp->filename) + lemonStrlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = lemonStrlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); + /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + sp = rp->rhs[i]; + printf(" %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + printf("|%s", sp->subsym[j]->name); + } + } + /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); + /* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + struct symbol *sp; + int i, j; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + sp = rp->rhs[i]; + fprintf(fp," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + fprintf(fp,"|%s",sp->subsym[j]->name); + } + } + } +} + +/* #define TEST */ +#if 0 +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case SRCONFLICT: + case RRCONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SSCONFLICT: + fprintf(fp,"%*s shift %d ** Parsing conflict **", + indent,ap->sp->name,ap->x.stp->statenum); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->statenum); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#if 0 + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + for(i=0; insymbol; i++){ + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if( sp->type==NONTERMINAL ){ + fprintf(fp, ":"); + if( sp->lambda ){ + fprintf(fp, " "); + } + for(j=0; jnterminal; j++){ + if( sp->firstset && SetFind(sp->firstset, j) ){ + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + fprintf(fp, "\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + extern char *getenv(); + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[lemonStrlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->statenum; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(out,lineno,filename) +FILE *out; +int lineno; +char *filename; +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int *lineno; +{ + if( str==0 ) return; + while( *str ){ + putc(*str,out); + if( *str=='\n' ) (*lineno)++; + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp = 0; + + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else if( sp->destructor ){ + cp = sp->destructor; + fprintf(out,"{\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,sp->destLineno,lemp->filename); } + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) (*lineno)++; + fputc(*cp,out); + } + fprintf(out,"\n"); (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + fprintf(out,"}\n"); (*lineno)++; + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(char *zText, int n, int p1, int p2){ + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = lemonStrlen(zText); + } + if( n+sizeof(zInt)*2+used >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = realloc(z, alloced); + } + if( z==0 ) return ""; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && n>0 && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += lemonStrlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + if( rp->code==0 ){ + rp->code = "\n"; + rp->line = rp->ruleline; + } + + append_str(0,0,0,0); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + struct symbol *sp = rp->rhs[i]; + int dtnum; + if( sp->type==MULTITERMINAL ){ + dtnum = sp->subsym[0]->dtnum; + }else{ + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + if( rp->code ){ + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp?cp:""); + } +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp; + + /* Generate code to do the reduce action */ + if( rp->code ){ + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,rp->line,lemp->filename); } + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) (*lineno)++; + } /* End loop */ + fprintf(out,"}\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)calloc( arraysize, sizeof(char*) ); + for(i=0; ivartype ){ + maxdtlength = lemonStrlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = lemonStrlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ + sp->dtnum = 0; + continue; + } + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( lemonStrlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," int yyinit;\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->useCnt ){ + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + } + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + return p2->nAction - p1->nAction; +} + +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp){ + int j; + fprintf(out,"%s ::=", rp->lhs->name); + for(j=0; jnrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; knsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } +} + + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+1)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + if( lemp->wildcard ){ + fprintf(out,"#define YYWILDCARD %d\n", + lemp->wildcard->index); lineno++; + } + print_stack_union(out,lemp,&lineno,mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++; + if( lemp->stacksize ){ + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + fprintf(out, "#endif\n"); lineno++; + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = lemonStrlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + if( lemp->errsym->useCnt ){ + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + } + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = calloc(lemp->nstate*2, sizeof(ax[0])); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + n = acttab_size(pActtab); + for(i=j=0; instate + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_SHIFT_MAX %d\n", n-1); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_REDUCE_MAX %d\n", n-1); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + int mx = lemp->nterminal - 1; + while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } + for(i=0; i<=mx; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammar. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"", i); + writeRuleText(out, rp); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + if( once ){ + fprintf(out, " /* TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + if( once ){ + fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + } + fprintf(out," break;\n"); lineno++; + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d: /* %s */\n", + sp2->index, sp2->name); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + translate_code(lemp, rp); + } + /* First output rules other than the default: rule */ + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; /* Other rules with the same action */ + if( rp->code==0 ) continue; + if( rp->code[0]=='\n' && rp->code[1]==0 ) continue; /* Will be default: */ + fprintf(out," case %d: /* ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */\n"); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d: /* ", rp2->index); + writeRuleText(out, rp2); + fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + rp->code = 0; + } + /* Finally, output the default: rule. We choose as the default: all + ** empty actions. */ + fprintf(out," default:\n"); lineno++; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code==0 ) continue; + assert( rp->code[0]=='\n' && rp->code[1]==0 ); + fprintf(out," /* (%d) ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->index); lineno++; + } + fprintf(out," break;\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Except, there is no default if the wildcard token +** is a possible look-ahead. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + int usesWildcard; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + usesWildcard = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==SHIFT && ap->sp==lemp->wildcard ){ + usesWildcard = 1; + } + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp->lhsStart ) continue; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 1 or if the wildcard token is a possible + ** lookahead. + */ + if( nbest<1 || usesWildcard ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + + +/* +** Compare two states for sorting purposes. The smaller state is the +** one with the most non-terminal actions. If they have the same number +** of non-terminal actions, then the smaller is the one with the most +** token actions. +*/ +static int stateResortCompare(const void *a, const void *b){ + const struct state *pA = *(const struct state**)a; + const struct state *pB = *(const struct state**)b; + int n; + + n = pB->nNtAct - pA->nNtAct; + if( n==0 ){ + n = pB->nTknAct - pA->nTknAct; + } + return n; +} + + +/* +** Renumber and resort states so that states with fewer choices +** occur at the end. Except, keep state 0 as the first state. +*/ +void ResortStates(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct action *ap; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + } + qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]), + stateResortCompare); + for(i=0; instate; i++){ + lemp->sorted[i]->statenum = i; + } +} + + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + s = (char*)calloc( size, 1); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + return s; +} + +/* Deallocate a set */ +void SetFree(s) +char *s; +{ + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(s,e) +char *s; +int e; +{ + int rv; + assert( e>=0 && esize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)calloc(1, sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = LEMON_FALSE; + sp->destructor = 0; + sp->destLineno = 0; + sp->datatype = 0; + sp->useCnt = 0; + Symbol_insert(sp,sp->name); + } + sp->useCnt++; + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(struct symbol **a, struct symbol **b){ + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)calloc(size, sizeof(struct symbol *)); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)calloc(1, sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/app/src/main/jni/badvpn/lemon/lempar.c b/app/src/main/jni/badvpn/lemon/lempar.c new file mode 100644 index 0000000..774b875 --- /dev/null +++ b/app/src/main/jni/badvpn/lemon/lempar.c @@ -0,0 +1,842 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/app/src/main/jni/badvpn/lime/HOWTO b/app/src/main/jni/badvpn/lime/HOWTO new file mode 100644 index 0000000..f447c84 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/HOWTO @@ -0,0 +1,70 @@ +Lime: An LALR(1) parser generator in and for PHP. + +Interpretter pattern got you down? Time to use a real parser? Welcome to Lime. + +If you're familiar with BISON or YACC, you may want to read the metagrammar. +It's written in the Lime input language, so you'll get a head-start on +understanding how to use Lime. + +0. If you're not running Linux on an IA32 box, then you will have to rebuild + lime_scan_tokens for your system. It should be enough to erase it, + and then type "CFLAGS=-O2 make lime_scan_tokens" at the bash prompt. + +1. Stare at the file lime/metagrammar to understand the syntax. You're seeing + slightly modified and tweaked Backus-Naur forms. The main differences + are that you get to name your components, instead of refering to them + by numbers the way that BISON demands. This idea was stolen from the + C-based "Lemon" parser from which Lime derives its name. Incidentally, + the author of Lemon disclaimed copyright, so you get a copy of the C + code that taught me LALR(1) parsing better than any book, despite the + obvious difficulties in understanding it. Oh, and one other thing: + symbols are terminal if the scanner feeds them to the parser. They + are non-terminal if they appear on the left side of a production rule. + Lime names semantic categories using strings instead of the numbers + that BISON-based parsers use, so you don't have to declare any list of + terminal symbols anywhere. + +2. Look at the file lime/lime.php to see what pragmas are defined. To be more + specific, you might look at the method lime::pragma(), which at the + time of this writing, supports "%left", "%right", "%nonassoc", + "%start", and "%class". The first three are for operator precedence. + The last two declare the start symbol and the name of a PHP class to + generate which will hold all the bottom-up parsing tables. + +3. Write a grammar file. + +4. php /path/to/lime/lime.php list-of-grammar-files > my_parser.php + +5. Read the function parse_lime_grammar() in lime.php to understand + how to integrate your parser into your program. + +6. Integrate your parser as follows: + +--------------- CUT --------------- + +include_once "lime/parse_engine.php"; +include_once "my_parser.php"; +# +# Later: +# +$parser = new parse_engine(new my_parser()); +# +# And still later: +# +try { + while (..something..) { + $parser->eat($type, $val); + # You figure out how to get the parameters. + } + # And after the last token has been eaten: + $parser->eat_eof(); +} catch (parse_error $e) { + die($e->getMessage()); +} +return $parser->semantic; + +--------------- CUT --------------- + +7. You now have the computed semantic value of whatever you parsed. Add salt + and pepper to taste, and serve. + diff --git a/app/src/main/jni/badvpn/lime/flex_token_stream.php b/app/src/main/jni/badvpn/lime/flex_token_stream.php new file mode 100644 index 0000000..c21e411 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/flex_token_stream.php @@ -0,0 +1,34 @@ +executable(); + $tokens = explode("\0", `$scanner < "\$PHP_LIME_SCAN_STDIN"`); + array_pop($tokens); + $this->tokens = $tokens; + $this->lineno = 1; + } + function next() { + if (list($key, $token) = each($this->tokens)) { + list($this->lineno, $type, $text) = explode("\1", $token); + return array($type, $text); + } + } + function feed($parser) { + while (list($type, $text) = $this->next()) { + $parser->eat($type, $text); + } + return $parser->eat_eof(); + } +} diff --git a/app/src/main/jni/badvpn/lime/lemon.c b/app/src/main/jni/badvpn/lime/lemon.c new file mode 100644 index 0000000..708b353 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/lemon.c @@ -0,0 +1,4588 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +char *msort(); +extern void *malloc(); + +/******** From the file "action.h" *************************************/ +struct action *Action_new(); +struct action *Action_sort(); + +/********* From the file "assert.h" ************************************/ +void myassert(); +#ifndef NDEBUG +# define assert(X) if(!(X))myassert(__FILE__,__LINE__) +#else +# define assert(X) +#endif + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + char *arg; + char *message; +}; +int OptInit(/* char**,struct s_options*,FILE* */); +int OptNArgs(/* void */); +char *OptArg(/* int */); +void OptErr(/* int */); +void OptPrint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {B_FALSE=0, B_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if predecence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destructorln; /* Line number of destructor code */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + CONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int index; /* Sequencial number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + int includeln; /* Line number for start of include code */ + char *error; /* Code to execute when an error is seen */ + int errorln; /* Line number for start of error code */ + char *overflow; /* Code to execute on a stack overflow */ + int overflowln; /* Line number for start of overflow code */ + char *failure; /* Code to execute on parser failure */ + int failureln; /* Line number for start of failure code */ + char *accept; /* Code to execute when the parser excepts */ + int acceptln; /* Line number for the start of accept code */ + char *extracode; /* Code appended to the generated file */ + int extracodeln; /* Line number for the start of the extra code */ + char *tokendest; /* Code to execute to destroy token data */ + int tokendestln; /* Line number for token destroyer code */ + char *vardest; /* Code for the default non-terminal destructor */ + int vardestln; /* Line number for default non-term destructor code*/ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammer */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +struct action *Action_new(){ + static struct action *freelist = 0; + struct action *new; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)malloc( sizeof(struct action)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Compare two actions */ +static int actioncmp(ap1,ap2) +struct action *ap1; +struct action *ap2; +{ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ) rc = (int)ap1->type - (int)ap2->type; + if( rc==0 ){ + assert( ap1->type==REDUCE || ap1->type==RD_RESOLVED || ap1->type==CONFLICT); + assert( ap2->type==REDUCE || ap2->type==RD_RESOLVED || ap2->type==CONFLICT); + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +struct action *Action_sort(ap) +struct action *ap; +{ + ap = (struct action *)msort((char *)ap,(char **)&ap->next,actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +char *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure +*/ +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ + } *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = malloc( sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset where we can + ** insert the current transaction set. Fall out of the loop when that + ** offset is found. In the worst case, we fall out of the loop when + ** i reaches p->nAction, which means we append the new transaction set. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=0; inAction+p->mnLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + }else if( p->aAction[i].lookahead==p->mnLookahead ){ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* Same as a prior transaction set */ + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "assert.c" ****************************/ +/* +** A more efficient way of handling assertions. +*/ +void myassert(file,line) +char *file; +int line; +{ + fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file); + exit(1); +} +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->prec>=0 ){ + rp->precsym = rp->rhs[i]; + break; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = B_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->lambda==B_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = B_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s1==s2 ){ + if( s1->lambda==B_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==B_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->index = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( bsp!=sp ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + assert( stp->ap ); + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = B_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = B_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolve, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = CONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==CONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==CONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)malloc( sizeof(struct config)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==B_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = strlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = strlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = malloc( strlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + + +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.has_fallback = 0; + lem.nconflict = 0; + lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0; + lem.vartype = 0; + lem.stacksize = 0; + lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest = + lem.tokenprefix = lem.outname = lem.extracode = 0; + lem.vardest = 0; + lem.tablesize = 0; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.rule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); + return (lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((unsigned long)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge(a,b,cmp,offset) +char *a; +char *b; +int (*cmp)(); +int offset; +{ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +char *msort(list,next,cmp) +char *list; +char **next; +int (*cmp)(); +{ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is not prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)malloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbol on RHS or rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + psp->decllnslot = &psp->gp->includeln; + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + psp->decllnslot = &psp->gp->extracodeln; + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + psp->decllnslot = &psp->gp->tokendestln; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + psp->decllnslot = &psp->gp->vardestln; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + psp->decllnslot = &psp->gp->errorln; + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + psp->decllnslot = &psp->gp->acceptln; + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + psp->decllnslot = &psp->gp->failureln; + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + psp->decllnslot = &psp->gp->overflowln; + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllnslot = &sp->destructorln; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){ + if( *(psp->declargslot)!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The argument \"%s\" to declaration \"%%%s\" is not the first.", + x[0]=='\"' ? &x[1] : x,psp->declkeyword); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x; + if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno; + psp->state = WAITING_FOR_DECL_OR_RULE; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the proprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start; + int lineno = 1; + int start_lineno; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = strlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); +/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + printf(" %s",rp->rhs[i]->name); +/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); +/* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + int i; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + fprintf(fp," %s",rp->rhs[i]->name); + } +} + +/* #define TEST */ +#ifdef TEST +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case CONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + fprintf(fp," \b"); + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->index); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#ifdef TEST + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + extern int access(); + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( strlen(argv0) + strlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + extern char *getenv(); + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( strlen(pathlist)+strlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[strlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->index; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(out,lineno,filename) +FILE *out; +int lineno; +char *filename; +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,strln,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int strln; +int *lineno; +{ + if( str==0 ) return; + tplt_linedir(out,strln,lemp->filename); + (*lineno)++; + while( *str ){ + if( *str=='\n' ) (*lineno)++; + putc(*str,out); + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + tplt_linedir(out,*lineno+2,lemp->outname); + (*lineno)+=2; + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp = 0; + + int linecnt = 0; + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + tplt_linedir(out,lemp->tokendestln,lemp->filename); + fprintf(out,"{"); + }else if( sp->destructor ){ + cp = sp->destructor; + tplt_linedir(out,sp->destructorln,lemp->filename); + fprintf(out,"{"); + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + tplt_linedir(out,lemp->vardestln,lemp->filename); + fprintf(out,"{"); + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } + (*lineno) += 3 + linecnt; + fprintf(out,"}\n"); + tplt_linedir(out,*lineno,lemp->outname); + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(char *zText, int n, int p1, int p2){ + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = strlen(zText); + } + if( n+sizeof(zInt)*2+used >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = realloc(z, alloced); + } + if( z==0 ) return ""; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += strlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + append_str(0,0,0,0); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + append_str("yymsp[%d].minor.yy%d",0, + i-rp->nrhs+1,rp->rhs[i]->dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp); +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp; + int linecnt = 0; + + /* Generate code to do the reduce action */ + if( rp->code ){ + tplt_linedir(out,rp->line,lemp->filename); + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) linecnt++; + } /* End loop */ + (*lineno) += 3 + linecnt; + fprintf(out,"}\n"); + tplt_linedir(out,*lineno,lemp->outname); + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)malloc( arraysize * sizeof(char*) ); + for(i=0; ivartype ){ + maxdtlength = strlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = strlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( strlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->dtnum); lineno++; + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + return p2->nAction - p1->nAction; +} + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+5)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + print_stack_union(out,lemp,&lineno,mhflag); + if( lemp->stacksize ){ + if( atoi(lemp->stacksize)<=0 ){ + ErrorMsg(lemp->filename,0, +"Illegal stack size: [%s]. The stack size should be an integer constant.", + lemp->stacksize); + lemp->errorcnt++; + lemp->stacksize = "100"; + } + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = strlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = malloc( sizeof(ax[0])*lemp->nstate*2 ); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + n = acttab_size(pActtab); + for(i=j=0; insymbol + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + for(i=0; interminal; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammer. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"%s ::=", i, rp->lhs->name); + for(j=0; jnrhs; j++) fprintf(out," %s",rp->rhs[j]->name); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d:\n",sp2->index); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code ) translate_code(lemp, rp); + } + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; + if( rp->code==0 ) continue; + fprintf(out," case %d:\n",rp->index); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d:\n",rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Only default a reduce if there are more than one. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 2 */ + if( nbest<2 ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + int i; + s = (char*)malloc( size ); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + for(i=0; isize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)malloc( sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = B_FALSE; + sp->destructor = 0; + sp->datatype = 0; + Symbol_insert(sp,sp->name); + } + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(struct symbol **a, struct symbol **b){ + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)malloc( sizeof(struct symbol *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)malloc( sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/app/src/main/jni/badvpn/lime/lime.bootstrap b/app/src/main/jni/badvpn/lime/lime.bootstrap new file mode 100644 index 0000000..63791c7 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/lime.bootstrap @@ -0,0 +1,31 @@ +There is nothing to see here. Go and look at the file called "metagrammar". + +: $$ = new lime(); +grammar pragma toklist stop : $$->pragma($2, $3); +grammar rewrite stop : $2->update($$); +to grammar +: {$$=array();} +toklist sym : $$[] = $2; +toklist lit : $$[] = $2; +to toklist +sym '=' rhs : $$ = new lime_rewrite($1); $$->add_rhs($3); +rewrite '|' rhs : $$->add_rhs($3); +to rewrite +list : $$ = new lime_rhs($1, ''); +list action : $$ = new lime_rhs($1, $2); +to rhs +action : $$ = new lime_action($1, NULL); +action lambda : $$ = new lime_action($1, $2); +sym : $$ = new lime_glyph($1, NULL); +sym lambda : $$ = new lime_glyph($1, $2); +lit : $$ = new lime_glyph($1, NULL); +to slot +: $$ = new lime_rhs(); +rhs slot : $$->add($2); +to rhs +'{' code '}' : $$ = $2; +to action +: +code php : $$.=$2; +code '{' code '}' : $$.='{'.$3.'}'; +to code diff --git a/app/src/main/jni/badvpn/lime/lime.php b/app/src/main/jni/badvpn/lime/lime.php new file mode 100644 index 0000000..b049225 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/lime.php @@ -0,0 +1,910 @@ +code=$code; } +} +class step { + /* + Base class for parse table instructions. The main idea is to make the + subclasses responsible for conflict resolution among themselves. It also + forms a sort of interface to the parse table. + */ + function __construct($sym) { + bug_unless($sym instanceof sym); + $this->sym = $sym; + } + function glyph() { return $this->sym->name; } +} +class error extends step { + function sane() { return false; } + function instruction() { bug("This should not happen."); } + function decide($that) { return $this; /* An error shall remain one. */ } +} +class shift extends step { + function __construct($sym, $q) { + parent::__construct($sym); + $this->q = $q; + } + function sane() { return true; } + function instruction() { return "s $this->q"; } + function decide($that) { + # shift-shift conflicts are impossible. + # shift-accept conflicts are a bug. + # so we can infer: + bug_unless($that instanceof reduce); + + # That being said, the resolution is a matter of precedence. + $shift_prec = $this->sym->right_prec; + $reduce_prec = $that->rule->prec; + + # If we don't have defined precedence levels for both options, + # then we default to shifting: + if (!($shift_prec and $reduce_prec)) return $this; + + # Otherwise, use the step with higher precedence. + if ($shift_prec > $reduce_prec) return $this; + if ($reduce_prec > $shift_prec) return $that; + + # The "nonassoc" works by giving equal precedence to both options, + # which means to put an error instruction in the parse table. + return new error($this->sym); + } +} +class reduce extends step { + function __construct($sym, $rule) { + bug_unless($rule instanceof rule); + parent::__construct($sym); + $this->rule = $rule; + } + function sane() { return true; } + function instruction() { return 'r '.$this->rule->id; } + function decide($that) { + # This means that the input grammar has a reduce-reduce conflict. + # Such things are considered an error in the input. + throw new RRC($this, $that); + #exit(1); + # BISON would go with the first encountered reduce thus: + # return $this; + } +} +class accept extends step { + function __construct($sym) { parent::__construct($sym); } + function sane() { return true; } + function instruction() { return 'a '.$this->sym->name; } +} +class RRC extends Exception { + function __construct($a, $b) { + parent::__construct("Reduce-Reduce Conflict"); + $this->a = $a; + $this->b = $b; + } + function make_noise() { + emit(sprintf( + "Reduce-Reduce Conflict:\n%s\n%s\nLookahead is (%s)", + $this->a->rule->text(), + $this->b->rule->text(), + $this->a->glyph() + )); + } +} +class state { + function __construct($id, $key, $close) { + $this->id = $id; + $this->key = $key; + $this->close = $close; # config key -> object + ksort($this->close); + $this->action = array(); + } + function dump() { + echo " * ".$this->id.' / '.$this->key."\n"; + foreach ($this->close as $config) $config->dump(); + } + function add_shift($sym, $state) { + $this->add_instruction(new shift($sym, $state->id)); + } + function add_reduce($sym, $rule) { + $this->add_instruction(new reduce($sym, $rule)); + } + function add_accept($sym) { + $this->add_instruction(new accept($sym)); + } + function add_instruction($step) { + bug_unless($step instanceof step); + $this->action[] = $step; + } + function find_reductions($lime) { + # rightmost configurations followset yields reduce. + foreach($this->close as $c) { + if ($c->rightmost) { + foreach ($c->follow->all() as $glyph) $this->add_reduce($lime->sym($glyph), $c->rule); + } + } + } + function resolve_conflicts() { + # For each possible lookahead, find one (and only one) step to take. + $table = array(); + foreach ($this->action as $step) { + $glyph = $step->glyph(); + if (isset($table[$glyph])) { + # There's a conflict. The shifts all came first, which + # simplifies the coding for the step->decide() methods. + try { + $table[$glyph] = $table[$glyph]->decide($step); + } catch (RRC $e) { + emit("State $this->id:"); + $e->make_noise(); + } + } else { + # This glyph is yet unprocessed, so the step at hand is + # our best current guess at what the grammar indicates. + $table[$glyph] = $step; + } + } + + # Now that we have the correct steps chosen, this routine is oddly + # also responsible for turning that table into the form that will + # eventually be passed to the parse engine. (So FIXME?) + $out = array(); + foreach ($table as $glyph => $step) { + if ($step->sane()) $out[$glyph] = $step->instruction(); + } + return $out; + } + function segment_config() { + # Filter $this->close into categories based on the symbol_after_the_dot. + $f = array(); + foreach ($this->close as $c) { + $p = $c->symbol_after_the_dot; + if (!$p) continue; + $f[$p->name][] = $c; + } + return $f; + } +} +class sym { + function __construct($name, $id) { + $this->name=$name; + $this->id=$id; + $this->term = true; # Until proven otherwise. + $this->rule = array(); + $this->config = array(); + $this->lambda = false; + $this->first = new set(); + $this->left_prec = $this->right_prec = 0; + } + function summary() { + $out = ''; + foreach ($this->rule as $rule) $out .= $rule->text()."\n"; + return $out; + } +} +class rule { + function __construct($id, $sym, $rhs, $code, $look, $replace) { + $this->id = $id; + $this->sym = $sym; + $this->rhs = $rhs; + $this->code = $code; + $this->look = $look; + bug_unless(is_int($look)); + $this->replace = $replace; + #$this->prec_sym = $prec_sym; + $this->prec = 0; + $this->first = array(); + $this->epsilon = count($rhs); + } + function lhs_glyph() { return $this->sym->name; } + function determine_precedence() { + # We may eventually expand to allow explicit prec_symbol declarations. + # Until then, we'll go with the rightmost terminal, which is what + # BISON does. People probably expect that. The leftmost terminal + # is a reasonable alternative behaviour, but I don't see the big + # deal just now. + + #$prec_sym = $this->prec_sym; + #if (!$prec_sym) + $prec_sym = $this->rightmost_terminal(); + if (!$prec_sym) return; + $this->prec = $prec_sym->left_prec; + } + private function rightmost_terminal() { + $symbol = NULL; + $rhs = $this->rhs; + while ($rhs) { + $symbol = array_pop($rhs); + if ($symbol->term) break; + } + return $symbol; + } + function text() { + $t = "($this->id) ".$this->lhs_glyph().' :='; + foreach($this->rhs as $s) $t .= ' '.$s->name; + return $t; + } + function table(lime_language $lang) { + return array( + 'symbol' => $this->lhs_glyph(), + 'len' => $this->look, + 'replace' => $this->replace, + 'code' => $lang->fixup($this->code), + 'text' => $this->text(), + ); + } + function lambda() { + foreach ($this->rhs as $sym) if (!$sym->lambda) return false; + return true; + } + function find_first() { + $dot = count($this->rhs); + $last = $this->first[$dot] = new set(); + while ($dot) { + $dot--; + $symbol_after_the_dot = $this->rhs[$dot]; + $first = $symbol_after_the_dot->first->all(); + bug_if(empty($first) and !$symbol_after_the_dot->lambda); + $set = new set($first); + if ($symbol_after_the_dot->lambda) { + $set->union($last); + if ($this->epsilon == $dot+1) $this->epsilon = $dot; + } + $last = $this->first[$dot] = $set; + } + } + function teach_symbol_of_first_set() { + $go = false; + foreach ($this->rhs as $sym) { + if ($this->sym->first->union($sym->first)) $go = true; + if (!$sym->lambda) break; + } + return $go; + } + function lambda_from($dot) { + return $this->epsilon <= $dot; + } + function leftmost($follow) { + return new config($this, 0, $follow); + } + function dotted_text($dot) { + $out = $this->lhs_glyph().' :='; + $idx = -1; + foreach($this->rhs as $idx => $s) { + if ($idx == $dot) $out .= ' .'; + $out .= ' '.$s->name; + } + if ($dot > $idx) $out .= ' .'; + return $out; + } +} +class config { + function __construct($rule, $dot, $follow) { + $this->rule=$rule; + $this->dot = $dot; + $this->key = "$rule->id.$dot"; + $this->rightmost = count($rule->rhs) <= $dot; + $this->symbol_after_the_dot = $this->rightmost ? null : $rule->rhs[$dot]; + $this->_blink = array(); + $this->follow = new set($follow); + $this->_flink= array(); + bug_unless($this->rightmost or count($rule)); + } + function text() { + $out = $this->rule->dotted_text($this->dot); + $out .= ' [ '.implode(' ', $this->follow->all()).' ]'; + return $out; + } + function blink($config) { + $this->_blink[] = $config; + } + function next() { + bug_if($this->rightmost); + $c = new config($this->rule, $this->dot+1, array()); + # Anything in the follow set for this config will also be in the next. + # However, we link it backwards because we might wind up selecting a + # pre-existing state, and the housekeeping is easier in the first half + # of the program. We'll fix it before doing the propagation. + $c->blink($this); + return $c; + } + function copy_links_from($that) { + foreach($that->_blink as $c) $this->blink($c); + } + function lambda() { + return $this->rule->lambda_from($this->dot); + } + function simple_follow() { + return $this->rule->first[$this->dot+1]->all(); + } + function epsilon_follows() { + return $this->rule->lambda_from($this->dot+1); + } + function fixlinks() { + foreach ($this->_blink as $that) $that->_flink[] = $this; + $this->blink = array(); + } + function dump() { + echo " * "; + echo $this->key.' : '; + echo $this->rule->dotted_text($this->dot); + echo $this->follow->text(); + foreach ($this->_flink as $c) echo $c->key.' / '; + echo "\n"; + } +} +class lime { + var $parser_class = 'parser'; + function __construct() { + $this->p_next = 1; + $this->sym = array(); + $this->rule = array(); + $this->start_symbol_set = array(); + $this->state = array(); + $this->stop = $this->sym('#'); + #$err = $this->sym('error'); + $err->term = false; + $this->lang = new lime_language_php(); + } + function language() { return $this->lang; } + function build_parser() { + $this->add_start_rule(); + foreach ($this->rule as $r) $r->determine_precedence(); + $this->find_sym_lamdba(); + $this->find_sym_first(); + foreach ($this->rule as $rule) $rule->find_first(); + $initial = $this->find_states(); + $this->fixlinks(); + # $this->dump_configurations(); + $this->find_follow_sets(); + foreach($this->state as $s) $s->find_reductions($this); + $i = $this->resolve_conflicts(); + $a = $this->rule_table(); + $qi = $initial->id; + return $this->lang->ptab_to_class($this->parser_class, compact('a', 'qi', 'i')); + } + function rule_table() { + $s = array(); + foreach ($this->rule as $i => $r) { + $s[$i] = $r->table($this->lang); + } + return $s; + } + function add_rule($symbol, $rhs, $code) { + $this->add_raw_rule($symbol, $rhs, $code, count($rhs), true); + } + function trump_up_bogus_lhs($real) { + return "'$real'".count($this->rule); + } + function add_raw_rule($lhs, $rhs, $code, $look, $replace) { + $sym = $this->sym($lhs); + $sym->term=false; + if (empty($rhs)) $sym->lambda = true; + $rs = array(); + foreach ($rhs as $str) $rs[] = $this->sym($str); + $rid = count($this->rule); + $r = new rule($rid, $sym, $rs, $code, $look, $replace); + $this->rule[$rid] = $r; + $sym->rule[] = $r; + } + function sym($str) { + if (!isset($this->sym[$str])) $this->sym[$str] = new sym($str, count($this->sym)); + return $this->sym[$str]; + } + function summary() { + $out = ''; + foreach ($this->sym as $sym) if (!$sym->term) $out .= $sym->summary(); + return $out; + } + private function find_sym_lamdba() { + do { + $go = false; + foreach ($this->sym as $sym) if (!$sym->lambda) { + foreach ($sym->rule as $rule) if ($rule->lambda()) { + $go = true; + $sym->lambda = true; + } + } + } while ($go); + } + private function teach_terminals_first_set() { + foreach ($this->sym as $sym) if ($sym->term) $sym->first->add($sym->name); + } + private function find_sym_first() { + $this->teach_terminals_first_set(); + do { + $go = false; + foreach ($this->rule as $r) if ($r->teach_symbol_of_first_set()) $go = true; + } while ($go); + } + function add_start_rule() { + $rewrite = new lime_rewrite("'start'"); + $rhs = new lime_rhs(); + $rhs->add(new lime_glyph($this->deduce_start_symbol()->name, NULL)); + #$rhs->add(new lime_glyph($this->stop->name, NULL)); + $rewrite->add_rhs($rhs); + $rewrite->update($this); + } + private function deduce_start_symbol() { + $candidate = current($this->start_symbol_set); + # Did the person try to set a start symbol at all? + if (!$candidate) return $this->first_rule_lhs(); + # Do we actually have such a symbol on the left of a rule? + if ($candidate->terminal) return $this->first_rule_lhs(); + # Ok, it's a decent choice. We need to return the symbol entry. + return $this->sym($candidate); + } + private function first_rule_lhs() { + reset($this->rule); + $r = current($this->rule); + return $r->sym; + } + function find_states() { + /* + Build an initial state. This is a recursive process which digs out + the LR(0) state graph. + */ + $start_glyph = "'start'"; + $sym = $this->sym($start_glyph); + $basis = array(); + foreach($sym->rule as $rule) { + $c = $rule->leftmost(array('#')); + $basis[$c->key] = $c; + } + $initial = $this->get_state($basis); + $initial->add_accept($sym); + return $initial; + } + function get_state($basis) { + $key = array_keys($basis); + sort($key); + $key = implode(' ', $key); + if (isset($this->state[$key])) { + # Copy all the links around... + $state = $this->state[$key]; + foreach($basis as $config) $state->close[$config->key]->copy_links_from($config); + return $state; + } else { + $close = $this->state_closure($basis); + $this->state[$key] = $state = new state(count($this->state), $key, $close); + $this->build_shifts($state); + return $state; + } + } + private function state_closure($q) { + # $q is a list of config. + $close = array(); + while ($config = array_pop($q)) { + if (isset($close[$config->key])) { + $close[$config->key]->copy_links_from($config); + $close[$config->key]->follow->union($config->follow); + continue; + } + $close[$config->key] = $config; + + $symbol_after_the_dot = $config->symbol_after_the_dot; + if (!$symbol_after_the_dot) continue; + + if (! $symbol_after_the_dot->term) { + foreach ($symbol_after_the_dot->rule as $r) { + $station = $r->leftmost($config->simple_follow()); + if ($config->epsilon_follows()) $station->blink($config); + $q[] = $station; + } + # The following turned out to be wrong. Don't do it. + #if ($symbol_after_the_dot->lambda) { + # $q[] = $config->next(); + #} + } + + } + return $close; + } + function build_shifts($state) { + foreach ($state->segment_config() as $glyph => $segment) { + $basis = array(); + foreach ($segment as $preshift) { + $postshift = $preshift->next(); + $basis[$postshift->key] = $postshift; + } + $dest = $this->get_state($basis); + $state->add_shift($this->sym($glyph), $dest); + } + } + function fixlinks() { + foreach ($this->state as $s) foreach ($s->close as $c) $c->fixlinks(); + } + function find_follow_sets() { + $q = array(); + foreach ($this->state as $s) foreach ($s->close as $c) $q[] = $c; + while ($q) { + $c = array_shift($q); + foreach ($c->_flink as $d) { + if ($d->follow->union($c->follow)) $q[] = $d; + } + } + } + private function set_assoc($ss, $l, $r) { + $p = ($this->p_next++)*2; + foreach ($ss as $glyph) { + $s = $this->sym($glyph); + $s->left_prec = $p+$l; + $s->right_prec = $p+$r; + } + } + function left_assoc($ss) { $this->set_assoc($ss, 1, 0); } + function right_assoc($ss) { $this->set_assoc($ss, 0, 1); } + function non_assoc($ss) { $this->set_assoc($ss, 0, 0); } + private function resolve_conflicts() { + # For each state, try to find one and only one + # thing to do for any given lookahead. + $i = array(); + foreach ($this->state as $s) $i[$s->id] = $s->resolve_conflicts(); + return $i; + } + function dump_configurations() { + foreach ($this->state as $q) $q->dump(); + } + function dump_first_sets() { + foreach ($this->sym as $s) { + echo " * "; + echo $s->name.' : '; + echo $s->first->text(); + echo "\n"; + } + } + function add_rule_with_actions($lhs, $rhs) { + # First, make sure this thing is well-formed. + if(!is_object(end($rhs))) $rhs[] = new cf_action(''); + # Now, split it into chunks based on the actions. + $look = -1; + $subrule = array(); + $subsymbol = ''; + while (count($rhs)) { + $it = array_shift($rhs); + $look ++; + if (is_string($it)) { + $subrule[] = $it; + } else { + $code = $it->code; + # It's an action. + # Is it the last one? + if (count($rhs)) { + # no. + $subsymbol = $this->trump_up_bogus_lhs($lhs); + $this->add_raw_rule($subsymbol, $subrule, $code, $look, false); + $subrule = array($subsymbol); + } else { + # yes. + $this->add_raw_rule($lhs, $subrule, $code, $look, true); + } + } + } + } + function pragma($type, $args) { + switch ($type) { + case 'left': + $this->left_assoc($args); + break; + + case 'right': + $this->right_assoc($args); + break; + + case 'nonassoc': + $this->non_assoc($args); + break; + + case 'start': + $this->start_symbol_set = $args; + break; + + case 'class': + $this->parser_class = $args[0]; + break; + + default: + emit(sprintf("Bad Parser Pragma: (%s)", $type)); + exit(1); + } + } +} +class lime_language {} +class lime_language_php extends lime_language { + private function result_code($expr) { return "\$result = $expr;\n"; } + function default_result() { return $this->result_code('reset($tokens)'); } + function result_pos($pos) { return $this->result_code(lime_token_reference($pos)); } + function bind($name, $pos) { return "\$$name =& \$tokens[$pos];\n"; } + function fixup($code) { + $code = preg_replace_callback('/\\$(\d+)/', 'lime_token_reference_callback', $code); + $code = preg_replace('/\\$\\$/', '$result', $code); + return $code; + } + function to_php($code) { + return $code; + } + function ptab_to_class($parser_class, $ptab) { + $code = "class $parser_class extends lime_parser {\n"; + $code .= 'var $qi = '.var_export($ptab['qi'], true).";\n"; + $code .= 'var $i = '.var_export($ptab['i'], true).";\n"; + + + $rc = array(); + $method = array(); + $rules = array(); + foreach($ptab['a'] as $k => $a) { + $symbol = preg_replace('/[^\w]/', '', $a['symbol']); + $rn = ++$rc[$symbol]; + $mn = "reduce_${k}_${symbol}_${rn}"; + $method[$k] = $mn; + $comment = "#\n# $a[text]\n#\n"; + $php = $this->to_php($a['code']); + $code .= "function $mn(".LIME_CALL_PROTOCOL.") {\n$comment$php\n}\n\n"; + + + unset($a['code']); + unset($a['text']); + $rules[$k] = $a; + } + + $code .= 'var $method = '.var_export($method, true).";\n"; + $code .= 'var $a = '.var_export($rules, true).";\n"; + + + + $code .= "}\n"; + #echo $code; + return $code; + } +} +class lime_rhs { + function __construct() { + /** + Construct and add glyphs and actions in whatever order. + Then, add this to a lime_rewrite. + + Don't call install_rule. + The rewrite will do that for you when you "update" with it. + */ + $this->rhs = array(); + } + function add($slot) { + bug_unless($slot instanceof lime_slot); + $this->rhs[] = $slot; + } + function install_rule(lime $lime, $lhs) { + # This is the part that has to break the rule into subrules if necessary. + $rhs = $this->rhs; + # First, make sure this thing is well-formed. + if (!(end($rhs) instanceof lime_action)) $rhs[] = new lime_action('', NULL); + # Now, split it into chunks based on the actions. + + $lang = $lime->language(); + $result_code = $lang->default_result(); + $look = -1; + $subrule = array(); + $subsymbol = ''; + $preamble = ''; + while (count($rhs)) { + $it = array_shift($rhs); + $look ++; + if ($it instanceof lime_glyph) { + $subrule[] = $it->data; + } elseif ($it instanceof lime_action) { + $code = $it->data; + # It's an action. + # Is it the last one? + if (count($rhs)) { + # no. + $subsymbol = $lime->trump_up_bogus_lhs($lhs); + $action = $lang->default_result().$preamble.$code; + $lime->add_raw_rule($subsymbol, $subrule, $action, $look, false); + $subrule = array($subsymbol); + } else { + # yes. + $action = $result_code.$preamble.$code; + $lime->add_raw_rule($lhs, $subrule, $action, $look, true); + } + } else { + impossible(); + } + if ($it->name == '$') $result_code = $lang->result_pos($look); + elseif ($it->name) $preamble .= $lang->bind($it->name, $look); + } + } +} +class lime_rewrite { + function __construct($glyph) { + /** + Construct one of these with the name of the lhs. + Add some rhs-es to it. + Finally, "update" the lime you're building. + */ + $this->glyph = $glyph; + $this->rhs = array(); + } + function add_rhs($rhs) { + bug_unless($rhs instanceof lime_rhs); + $this->rhs[] = $rhs; + } + function update(lime $lime) { + foreach ($this->rhs as $rhs) { + $rhs->install_rule($lime, $this->glyph); + + } + } +} +class lime_slot { + /** + This keeps track of one position in an rhs. + We specialize to handle actions and glyphs. + If there is a name for the slot, we store it here. + Later on, this structure will be consulted in the formation of + actual production rules. + */ + function __construct($data, $name) { + $this->data = $data; + $this->name = $name; + } + function preamble($pos) { + if (strlen($this->name) > 0) { + return "\$$this->name =& \$tokens[$pos];\n"; + } + } +} +class lime_glyph extends lime_slot {} +class lime_action extends lime_slot {} +function lime_bootstrap() { + + /* + + This function isn't too terribly interesting to the casual observer. + You're probably better off looking at parse_lime_grammar() instead. + + Ok, if you insist, I'll explain. + + The input to Lime is a CFG parser definition. That definition is + written in some language. (The Lime language, to be exact.) + Anyway, I have to parse the Lime language and compile it into a + very complex data structure from which a parser is eventually + built. What better way than to use Lime itself to parse its own + language? Well, it's almost that simple, but not quite. + + The Lime language is fairly potent, but a restricted subset of + its features was used to write a metagrammar. Then, I hand-translated + that metagrammar into another form which is easy to snarf up. + In the process of reading that simplified form, this function + builds the same sort of data structure that later gets turned into + a parser. The last step is to run the parser generation algorithm, + eval() the resulting PHP code, and voila! With no hard work, I can + suddenly read and comprehend the full range of the Lime language + without ever having written an algorithm to do so. It feels like magic. + + */ + + $bootstrap = LIME_DIR."/lime.bootstrap"; + $lime = new lime(); + $lime->parser_class = 'lime_metaparser'; + $rhs = array(); + bug_unless(is_readable($bootstrap)); + foreach(file($bootstrap) as $l) { + $a = explode(":", $l, 2); + if (count($a) == 2) { + list($pattern, $code) = $a; + $sl = new lime_rhs(); + $pattern = trim($pattern); + if (strlen($pattern)>0) { + foreach (explode(' ', $pattern) as $glyph) $sl->add(new lime_glyph($glyph, NULL)); + } + $sl->add(new lime_action($code, NULL)); + $rhs[] = $sl; + } else { + $m = preg_match('/^to (\w+)$/', $l, $r); + if ($m == 0) continue; + $g = $r[1]; + $rw = new lime_rewrite($g); + foreach($rhs as $b) $rw->add_rhs($b); + $rw->update($lime); + $rhs = array(); + } + } + $parser_code = $lime->build_parser(); + eval($parser_code); +} + +class voodoo_scanner extends flex_scanner { + /* + + The voodoo is in the way I do lexical processing on grammar definition + files. They contain embedded bits of PHP, and it's important to keep + track of things like strings, comments, and matched braces. It seemed + like an ideal problem to solve with GNU flex, so I wrote a little + scanner in flex and C to dig out the tokens for me. Of course, I need + the tokens in PHP, so I designed a simple binary wrapper for them which + also contains line-number information, guaranteed to help out if you + write a grammar which surprises the parser in any manner. + + */ + function executable() { return LIME_DIR.'/lime_scan_tokens'; } +} + +function parse_lime_grammar($path) { + /* + + This is a good function to read because it teaches you how to interface + with a Lime parser. I've tried to isolate out the bits that aren't + instructive in that regard. + + */ + if (!class_exists('lime_metaparser')) lime_bootstrap(); + + $parse_engine = new parse_engine(new lime_metaparser()); + $scanner = new voodoo_scanner($path); + try { + # The result of parsing a Lime grammar is a Lime object. + $lime = $scanner->feed($parse_engine); + # Calling its build_parser() method gets the output PHP code. + return $lime->build_parser(); + } catch (parse_error $e) { + die ($e->getMessage()." in $path line $scanner->lineno.\n"); + } +} + + +if ($_SERVER['argv']) { + $code = ''; + array_shift($_SERVER['argv']); # Strip out the program name. + foreach ($_SERVER['argv'] as $path) { + $code .= parse_lime_grammar($path); + } + + echo " + +/* + +DON'T EDIT THIS FILE! + +This file was automatically generated by the Lime parser generator. +The real source code you should be looking at is in one or more +grammar files in the Lime format. + +THE ONLY REASON TO LOOK AT THIS FILE is to see where in the grammar +file that your error happened, because there are enough comments to +help you debug your grammar. + +If you ignore this warning, you're shooting yourself in the brain, +not the foot. + +*/ + +"{" { + lit(); + yy_push_state(code); +} + +. lit(); + + +{ +\n { + out("stop", "."); + yy_pop_state(); +} +[[:space:]] {} +{SYM} tok("sym"); +{LIT} tok("lit"); +. lit(); +} + +{ +"}" { + lit(); + yy_pop_state(); +} +'{SCHAR}*' php(); +\"{DCHAR}*\" php(); +{COM}.* php(); +{BLOCKCMT} php(); +[^{}'"#/]+ php(); +. php(); +} + +%% + +void lit() { + char lit[] = "'.'"; + lit[1] = *yytext; + out(lit, yytext); +} + +void tok(char*t) { + out(t, yytext); +} + +void php() { + out("php", yytext); +} + +void out(char*type, char*value) { + printf("%d\001%s\001%s", yylineno, type, value); + fputc(0, stdout); +} diff --git a/app/src/main/jni/badvpn/lime/metagrammar b/app/src/main/jni/badvpn/lime/metagrammar new file mode 100644 index 0000000..5d057c0 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/metagrammar @@ -0,0 +1,58 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# This is the grammar for all other grammar files that will work with the +# Lime LALR(1) Context-Free Grammar Parser Generator. +# You can read this to get an idea how things work, but this file is not +# actually used in the system. Rather, it's an implementation guide for the +# file "lime.bootstrap". + +%class lime_metaparser +%start grammar + +grammar += {$$ = new lime();} +| grammar/$ pragma/p toklist/t stop {$$->pragma($p, $t);} +| grammar/$ rewrite/r stop {$r->update($$);} +. + +rewrite += sym/s '=' rhs/r {$$ = new lime_rewrite($s); $$->add_rhs($r);} +| rewrite/$ '|' rhs/r {$$->add_rhs($r);} +. + +slot += action/a {$$ = new lime_action($a, NULL);} +| action/a lambda/l {$$ = new lime_action($a, $l);} +| sym/s {$$ = new lime_glyph($s, NULL);} +| sym/s lambda/l {$$ = new lime_glyph($s, $l);} +| lit/l {$$ = new lime_glyph($l, NULL);} +. + +rhs += {$$ = new lime_rhs();} +| rhs/$ slot/s {$$->add($s);} +. + +action = '{' code/$ '}' . + +toklist = {$$=array();} +| toklist/$ sym/s {$$[] = $s;} +| toklist/$ lit/l {$$[] = $l;} +. + +code = {} +| code/$ php/p {$$.=$p;} +| code/$ '{' code/c '}' {$$.='{'.$c.'}';} +. diff --git a/app/src/main/jni/badvpn/lime/parse_engine.php b/app/src/main/jni/badvpn/lime/parse_engine.php new file mode 100644 index 0000000..fd54cc4 --- /dev/null +++ b/app/src/main/jni/badvpn/lime/parse_engine.php @@ -0,0 +1,252 @@ +type = $type; + $this->state = $state; + } +} +class parse_premature_eof extends parse_error { + function __construct() { + parent::__construct("Premature EOF"); + } +} + + +class parse_stack { + function __construct($qi) { + $this->q = $qi; + $this->qs = array(); + $this->ss = array(); + } + function shift($q, $semantic) { + $this->ss[] = $semantic; + $this->qs[] = $this->q; + $this->q = $q; + # echo "Shift $q -- $semantic
\n"; + } + function top_n($n) { + if (!$n) return array(); + return array_slice($this->ss, 0-$n); + } + function pop_n($n) { + if (!$n) return array(); + $qq = array_splice($this->qs, 0-$n); + $this->q = $qq[0]; + return array_splice($this->ss, 0-$n); + } + function occupied() { return !empty($this->ss); } + function index($n) { + if ($n) $this->q = $this->qs[count($this->qs)-$n]; + } + function text() { + return $this->q." : ".implode(' . ', array_reverse($this->qs)); + } +} +class parse_engine { + function __construct($parser) { + $this->parser = $parser; + $this->qi = $parser->qi; + $this->rule = $parser->a; + $this->step = $parser->i; + #$this->prepare_callables(); + $this->reset(); + #$this->debug = false; + } + function reset() { + $this->accept = false; + $this->stack = new parse_stack($this->qi); + } + private function enter_error_tolerant_state() { + while ($this->stack->occupied()) { + if ($this->has_step_for('error')) return true; + $this->drop(); + }; + return false; + } + private function drop() { $this->stack->pop_n(1); } + function eat_eof() { + {/* + + So that I don't get any brilliant misguided ideas: + + The "accept" step happens when we try to eat a start symbol. + That happens because the reductions up the stack at the end + finally (and symetrically) tell the parser to eat a symbol + representing what they've just shifted off the end of the stack + and reduced. However, that doesn't put the parser into any + special different state. Therefore, it's back at the start + state. + + That being said, the parser is ready to reduce an EOF to the + empty program, if given a grammar that allows them. + + So anyway, if you literally tell the parser to eat an EOF + symbol, then after it's done reducing and accepting the prior + program, it's going to think it has another symbol to deal with. + That is the EOF symbol, which means to reduce the empty program, + accept it, and then continue trying to eat the terminal EOF. + + This infinte loop quickly runs out of memory. + + That's why the real EOF algorithm doesn't try to pretend that + EOF is a terminal. Like the invented start symbol, it's special. + + Instead, we pretend to want to eat EOF, but never actually + try to get it into the parse stack. (It won't fit.) In short, + we look up what reduction is indicated at each step in the + process of rolling up the parse stack. + + The repetition is because one reduction is not guaranteed to + cascade into another and clean up the entire parse stack. + Rather, it will instead shift each partial production as it + is forced to completion by the EOF lookahead. + */} + + # We must reduce as if having read the EOF symbol + do { + # and we have to try at least once, because if nothing + # has ever been shifted, then the stack will be empty + # at the start. + list($opcode, $operand) = $this->step_for('#'); + switch ($opcode) { + case 'r': $this->reduce($operand); break; + case 'e': $this->premature_eof(); break; + default: throw new parse_bug(); break; + } + } while ($this->stack->occupied()); + {/* + If the sentence is well-formed according to the grammar, then + this will eventually result in eating a start symbol, which + causes the "accept" instruction to fire. Otherwise, the + step('#') method will indicate an error in the syntax, which + here means a premature EOF. + + Incedentally, some tremendous amount of voodoo with the parse + stack might help find the beginning of some unfinished + production that the sentence was cut off during, but as a + general rule that would require deeper knowledge. + */} + if (!$this->accept) throw new parse_bug(); + return $this->semantic; + } + private function premature_eof() { + $seen = array(); + while ($this->enter_error_tolerant_state()) { + if (isset($seen[$this->state()])) { + // This means that it's pointless to try here. + // We're guaranteed that the stack is occupied. + $this->drop(); + continue; + } + $seen[$this->state()] = true; + + $this->eat('error', NULL); + if ($this->has_step_for('#')) { + // Good. We can continue as normal. + return; + } else { + // That attempt to resolve the error condition + // did not work. There's no point trying to + // figure out how much to slice off the stack. + // The rest of the algorithm will make it happen. + } + } + throw new parse_premature_eof(); + } + private function current_row() { return $this->step[$this->state()]; } + private function step_for($type) { + $row = $this->current_row(); + if (!isset($row[$type])) return array('e', $this->stack->q); + return explode(' ', $row[$type]); + } + private function has_step_for($type) { + $row = $this->current_row(); + return isset($row[$type]); + } + private function state() { return $this->stack->q; } + function eat($type, $semantic) { + # assert('$type == trim($type)'); + # if ($this->debug) echo "Trying to eat a ($type)\n"; + list($opcode, $operand) = $this->step_for($type); + switch ($opcode) { + case 's': + # if ($this->debug) echo "shift $type to state $operand\n"; + $this->stack->shift($operand, $semantic); + # echo $this->stack->text()." shift $type
\n"; + break; + + case 'r': + $this->reduce($operand); + $this->eat($type, $semantic); + # Yes, this is tail-recursive. It's also the simplest way. + break; + + case 'a': + if ($this->stack->occupied()) throw new parse_bug('Accept should happen with empty stack.'); + $this->accept = true; + #if ($this->debug) echo ("Accept\n\n"); + $this->semantic = $semantic; + break; + + case 'e': + # This is thought to be the uncommon, exceptional path, so + # it's OK that this algorithm will cause the stack to + # flutter while the parse engine waits for an edible token. + # if ($this->debug) echo "($type) causes a problem.\n"; + if ($this->enter_error_tolerant_state()) { + $this->eat('error', NULL); + if ($this->has_step_for($type)) $this->eat($type, $semantic); + } else { + # If that didn't work, give up: + throw new parse_error("Parse Error: ($type)($semantic) not expected"); + } + break; + + default: + throw new parse_bug("Bad parse table instruction ".htmlspecialchars($opcode)); + } + } + private function reduce($rule_id) { + $rule = $this->rule[$rule_id]; + $len = $rule['len']; + $semantic = $this->perform_action($rule_id, $this->stack->top_n($len)); + #echo $semantic.br(); + if ($rule['replace']) $this->stack->pop_n($len); + else $this->stack->index($len); + $this->eat($rule['symbol'], $semantic); + } + private function perform_action($rule_id, $slice) { + # we have this weird calling convention.... + $result = null; + $method = $this->parser->method[$rule_id]; + #if ($this->debug) echo "rule $id: $method\n"; + $this->parser->$method($slice, $result); + return $result; + } +} diff --git a/app/src/main/jni/badvpn/lime/set.so.php b/app/src/main/jni/badvpn/lime/set.so.php new file mode 100644 index 0000000..ef87c6c --- /dev/null +++ b/app/src/main/jni/badvpn/lime/set.so.php @@ -0,0 +1,29 @@ +data = array_count_values($list); } + function has($item) { return isset($this->data[$item]); } + function add($item) { $this->data[$item] = true; } + function del($item) { unset($this->data[$item]); return $item;} + function all() { return array_keys($this->data); } + function one() { return key($this->data); } + function count() { return count($this->data); } + function pop() { return $this->del($this->one()); } + function union($that) { + $progress = false; + foreach ($that->all() as $item) if (!$this->has($item)) { + $this->add($item); + $progress = true; + } + return $progress; + } + function text() { + return ' { '.implode(' ', $this->all()).' } '; + } +} diff --git a/app/src/main/jni/badvpn/lwip/CHANGELOG b/app/src/main/jni/badvpn/lwip/CHANGELOG new file mode 100644 index 0000000..685b568 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/CHANGELOG @@ -0,0 +1,3396 @@ +HISTORY + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ New features: + + 2012-03-25: Simon Goldschmidt (idea by Mason) + * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h + which are a simple wrapper to the correct lwIP include files. + + 2012-01-16: Simon Goldschmidt + * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP + + 2011-12-17: Simon Goldschmidt + * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW) + (fixes bug #35061) + + 2011-09-27: Simon Goldschmidt + * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989) + (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h) + + 2011-09-21: Simon Goldschmidt + * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on + send (TCP only, bug #33820) + + 2011-09-21: Simon Goldschmidt + * init.c: Converted runtime-sanity-checks into compile-time checks that can + be disabled (since runtime checks can often not be seen on embedded targets) + + 2011-09-11: Simon Goldschmidt + * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file + to get a clear separation of which functions an application or port may use + (task #11281) + + 2011-09-11: Simon Goldschmidt + * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize + initial local TCP/UDP ports (so that different port ranges are used after + a reboot; bug #33818; this one added tcp_init/udp_init functions again) + + 2011-09-03: Simon Goldschmidt + * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302) + + 2011-08-24: Simon Goldschmidt + * opt.h, netif.h/.c: added netif remove callback (bug #32397) + + 2011-07-26: Simon Goldschmidt + * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter + function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN) + + 2011-07-21: Simon Goldschmidt (patch by hanhui) + * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour: + Added pbuf flags to mark incoming packets as link-layer broadcast/multicast. + Also added code to allow ip_forward() to forward non-broadcast packets to + the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1). + + 2011-07-21: Simon Goldschmidt + * sockets.c, opt.h: (bug #30185): added LWIP_FIONREAD_LINUXMODE that makes + ioctl/FIONREAD return the size of the next pending datagram. + + 2011-06-26: Simon Goldschmidt (patch by Cameron Gutman) + * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that + pcb->state != LISTEN + + 2011-05-25: Simon Goldschmidt + * again nearly the whole stack, renamed ip.c to ip4.c, ip_addr.c to ip4_addr.c, + combined ipv4/ipv6 inet_chksum.c, added ip.h, ip_addr.h: Combined IPv4 + and IPv6 code where possible, added defines to access IPv4/IPv6 in non-IP + code so that the code is more readable. + + 2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt) + * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to + Ivan! (this is work in progress: we're just post release anyway :-) + + 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage) + * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static + memory message + + + ++ Bugfixes: + + 2013-01-15: Simon Goldschmidt + * ip4.c: fixed bug #37665 ip_canforward operates on address in wrong byte order + + 2013-01-15: Simon Goldschmidt + * pbuf.h: fixed bug #38097 pbuf_free_ooseq() warning + + 2013-01-14: Simon Goldschmidt + * dns.c: fixed bug #37705 Possible memory corruption in DNS query + + 2013-01-11: Simon Goldschmidt + * raw.c: fixed bug #38066 Raw pcbs can alter packet without eating it + + 2012-09-26: Simon Goldschmidt + * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7843 Fix corner case with dhcp timeouts + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet + + 2012-08-22: Simon Goldschmidt + * memp.c: fixed bug #37166: memp_sanity check loops itself + + 2012-08-13: Simon Goldschmidt + * dhcp.c: fixed bug #36645: Calling dhcp_release before dhcp_start + dereferences NULL + + 2012-08-13: Simon Goldschmidt + * msg_out.c: fixed bug #36840 snmp_send_trap() NULL de-reference if traps + configured but no interfaces available + + 2012-08-13: Simon Goldschmidt + * dns.c: fixed bug #36899 DNS TTL 0 is cached for a long time + + 2012-05-11: Simon Goldschmidt (patch by Marty) + * memp.c: fixed bug #36412: memp.c does not compile when + MEMP_OVERFLOW_CHECK > zero and MEMP_SEPARATE_POOLS == 1 + + 2012-05-08: Simon Goldschmidt + * tcp_out.c: fixed bug #36380: unsent_oversize mismatch in 1.4.1RC1 (this was + a debug-check issue only) + + 2012-05-03: Simon Goldschmidt (patch by Sylvain Rochet) + * ppp.c: fixed bug #36283 (PPP struct used on header size computation and + not packed) + + 2012-05-03: Simon Goldschmidt (patch by David Empson) + * ppp.c: fixed bug #36388 (PPP: checksum-only in last pbuf leads to pbuf with + zero length) + + 2012-03-27: Simon Goldschmidt + * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c + + 2012-03-27: Simon Goldschmidt (patch by Mason) + * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the + send MSS + + 2012-03-25: Simon Goldschmidt + * api_msg.c: Fixed bug #35817: do_connect() invalidly signals op_completed + for UDP/RAW with LWIP_TCPIP_CORE_LOCKING==1 + + 2012-03-25: Simon Goldschmidt + * api_msg.h, api_lib.c, api_msg.c, netifapi.c: fixed bug #35931: Name space + pollution in api_msg.c and netifapi.c + + 2012-03-22: Simon Goldschmidt + * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward + + 2012-03-20: Simon Goldschmidt (patch by Mason) + * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list + + 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic) + * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian, + possible bug on little endian system + + 2012-02-23: Simon Goldschmidt + * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt + * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP() + (bug #35541: PPP Memory Leak) + + 2012-02-16: Simon Goldschmidt + * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage) + * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed + + 2012-02-15: Simon Goldschmidt + * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with + MEMP_MEM_MALLOC==1 + + 2012-02-12: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on + MSS > pcb->snd_wnd (by not creating segments bigger than half the window) + + 2012-02-11: Simon Goldschmidt + * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait + queue while closing + + 2012-01-22: Simon Goldschmidt + * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR) + + 2012-01-21: Simon Goldschmidt + * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb + + 2012-01-20: Simon Goldschmidt + * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths + + 2012-01-20: Simon Goldschmidt + * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy + + 2011-11-25: Simon Goldschmidt + * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt + tcp_active_pcbs in some cases + + 2011-11-23: Simon Goldschmidt + * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with + '#ifndef sys_msleep' + + 2011-11-22: Simon Goldschmidt + * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when + netif is brought down + + 2011-10-28: Simon Goldschmidt + * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks + + 2011-10-23: Simon Goldschmidt + * mem.c: fixed bug #34429: possible memory corruption with + LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1 + + 2011-10-18: Simon Goldschmidt + * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard + error value + + 2011-10-18: Simon Goldschmidt + * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small + windows (bug #34176 select after non-blocking send times out) + + 2011-10-18: Simon Goldschmidt + * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't + consider netif->mtu, causes slow network + + 2011-10-18: Simon Goldschmidt + * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code + + 2011-10-18: Simon Goldschmidt + * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS + + 2011-10-17: Simon Goldschmidt + * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api + + 2011-10-13: Simon Goldschmidt + * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no + zero window is received) by starting the persist timer when a zero window is + received, not when we have more data queued for sending than fits into the + window + + 2011-10-13: Simon Goldschmidt + * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex + + 2011-10-13: Simon Goldschmidt + * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is + used and not all protocols are enabled + + 2011-10-12: Simon Goldschmidt + * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4 + + 2011-10-09: Simon Goldschmidt + * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect + byte value when pcb->unacked != NULL + + 2011-10-09: Simon Goldschmidt + * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong + + 2011-09-27: Simon Goldschmidt + * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places... + + 2011-09-27: Simon Goldschmidt + * tcp_in.c: fixed bug #28288: Data after FIN in oos queue + + 2011-09-27: Simon Goldschmidt + * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf + + 2011-09-24: Simon Goldschmidt + * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1 + + 2011-09-23: Simon Goldschmidt + * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for + the last packet including FIN can lose data + + 2011-09-22: Simon Goldschmidt + * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into + account + + 2011-09-21: Simon Goldschmidt + * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks + in init.c + + 2011-09-20: Simon Goldschmidt + * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts) + + 2011-09-11: Simon Goldschmidt + * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs + (bug #34019) + + 2011-09-09: Simon Goldschmidt + * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if + udp port matches + + 2011-09-03: Simon Goldschmidt + * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet + is aggregated and sent to application + + 2011-09-01: Simon Goldschmidt + * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared + to other options + + 2011-09-01: Simon Goldschmidt + * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno + + 2011-08-24: Simon Goldschmidt + * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard + + 2011-08-24: Simon Goldschmidt + * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling + accept() on UDP connections + + 2011-08-24: Simon Goldschmidt + * sockets.h: fixed bug #34057 socklen_t should be a typedef + + 2011-08-24: Simon Goldschmidt + * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo) + + 2011-08-24: Simon Goldschmidt + * dhcp.c: fixed bug #34122 dhcp: hostname can overflow + + 2011-08-24: Simon Goldschmidt + * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr + + 2011-08-22: Simon Goldschmidt + * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This + merely prevents nagle from not transmitting fast after closing.) + + 2011-07-22: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns + always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now + lwip_send() sends as much as possible for non-blocking sockets + + 2011-07-22: Simon Goldschmidt + * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented + for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level. + + 2011-07-21: Simon Goldschmidt + * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by + sending an ARP request when an ARP entry is used in the last minute before + it would time out. + + 2011-07-04: Simon Goldschmidt + * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0. + + 2011-06-26: Simon Goldschmidt + * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by + updating its documentation only. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an + unaligned pointer. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1" + + + +(STABLE-1.4.0) + + ++ New features: + + 2011-03-27: Simon Goldschmidt + * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and + calculate it in tcp_zero_window_probe (the only place where it was used). + + 2010-11-21: Simon Goldschmidt + * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif + (fixes bug #31525). + + 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage) + * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for + IP_MULTICAST_LOOP at socket- and raw-API level. + + 2010-06-16: Simon Goldschmidt + * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow + link-layer-addressed UDP traffic to be received while a netif is down (just + like DHCP during configuration) + + 2010-05-22: Simon Goldschmidt + * many many files: bug #27352: removed packing from ip_addr_t, the packed + version is now only used in protocol headers. Added global storage for + current src/dest IP address while in input functions. + + 2010-05-16: Simon Goldschmidt + * def.h: task #10391: Add preprocessor-macros for compile-time htonl + calculation (and use them throughout the stack where applicable) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool + instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own + MEMP pool instead of the heap + + 2010-05-13: Simon Goldschmidt + * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added + new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast + packets to more than one pcb. + + 2010-05-02: Simon Goldschmidt + * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending + UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-04-30: Simon Goldschmidt + * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that + take a precalculated checksum, added pbuf_fill_chksum() to copy data + into a pbuf and at the same time calculating the checksum for that data + + 2010-04-29: Simon Goldschmidt + * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying + 2-byte-aligned IP addresses and MAC addresses + + 2010-04-28: Patch by Bill Auerbach + * ip.c: Inline generating IP checksum to save a function call + + 2010-04-14: Simon Goldschmidt + * tcpip.h/.c, timers.c: Added an overridable define to get informed when the + tcpip_thread processes messages or timeouts to implement a watchdog. + + 2010-03-28: Simon Goldschmidt + * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing + fragment if LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-03-27: Simon Goldschmidt + * etharp.c: Speedup TX by moving code from find_entry to etharp_output/ + etharp_query to prevent unnecessary function calls (inspired by + patch #7135). + + 2010-03-20: Simon Goldschmidt + * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code + since the linker cannot do this automatically to save space. + + 2010-03-20: Simon Goldschmidt + * opt.h, etharp.c/.h: Added support for static ARP table entries + + 2010-03-14: Simon Goldschmidt + * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum + when creating TCP segments, not when (re-)transmitting them. + + 2010-03-07: Simon Goldschmidt + * sockets.c: bug #28775 (select/event_callback: only check select_cb_list + on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code. + This should speed up receiving data on sockets as the select code in + event_callback is only executed when select is waiting. + + 2010-03-06: Simon Goldschmidt + * tcp_out.c: task #7013 (Create option to have all packets delivered to + netif->output in one piece): Always copy to try to create single pbufs + in tcp_write. + + 2010-03-06: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv + by not allocating a netbuf): added function netconn_recv_tcp_pbuf() + for tcp netconns to receive pbufs, not netbufs; use that function + for tcp sockets. + + 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt + * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040: + Work on tcp_enqueue: Don't waste memory when chaining segments, + added option TCP_OVERSIZE to prevent creating many small pbufs when + calling tcp_write with many small blocks of data. Instead, pbufs are + allocated larger than needed and the space is used for later calls to + tcp_write. + + 2010-02-21: Simon Goldschmidt + * stats.c/.h: Added const char* name to mem- and memp-stats for easier + debugging. + + 2010-02-21: Simon Goldschmidt + * tcp.h (and usages), added tcp_impl.h: Splitted API and internal + implementation of tcp to make API usage cleare to application programmers + + 2010-02-14: Simon Goldschmidt/Stephane Lesage + * ip_addr.h: Improved some defines working on ip addresses, added faster + macro to copy addresses that cannot be NULL + + 2010-02-13: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non- + blocking send operation) + + 2010-02-12: Simon Goldschmidt + * sockets.c/.h: Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + 2010-02-12: Simon Goldschmidt + * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated + memory): added autoip_set_struct() and dhcp_set_struct() to let autoip + and dhcp work with user-allocated structs instead of callin mem_malloc + + 2010-02-12: Simon Goldschmidt/Jeff Barber + * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has + SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT + + 2010-02-12: Simon Goldschmidt + * sys layer: task #10139 (Prefer statically allocated memory): converted + mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t; + converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX + to let sys.h use binary semaphores instead of mutexes - as before) + + 2010-02-09: Simon Goldschmidt (Simon Kallweit) + * timers.c/.h: Added function sys_restart_timeouts() from patch #7085 + (Restart system timeout handling) + + 2010-02-09: Simon Goldschmidt + * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into + netif.c) - loopif does not have to be created by the port any more, + just define LWIP_HAVE_LOOPIF to 1. + + 2010-02-08: Simon Goldschmidt + * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa + inet_ntoa_r/ipaddr_ntoa_r + + 2010-02-08: Simon Goldschmidt + * netif.h: Added netif_s/get_igmp_mac_filter() macros + + 2010-02-05: Simon Goldschmidt + * netif.h: Added function-like macros to get/set the hostname on a netif + + 2010-02-04: Simon Goldschmidt + * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to + make changing the actual implementation behind the typedef easier. + + 2010-02-01: Simon Goldschmidt + * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool + for allocating memory when getaddrinfo() is called. + + 2010-01-31: Simon Goldschmidt + * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse + them once instead of parsing for every option. This also removes + the need for mem_malloc from dhcp_recv and makes it possible to + correctly retrieve the BOOTP file. + + 2010-01-30: simon Goldschmidt + * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect + the sockets array. + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, api_msg.c, sockets.c: Added except set support in select + (patch #6860) + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c: + Add non-blocking support for connect (partly from patch #6860), + plus many cleanups in socket & netconn API. + + 2010-01-27: Simon Goldschmidt + * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding + to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 + + 2010-01-26: Simon Goldschmidt + * snmp: Use memp pools for snmp instead of the heap; added 4 new pools. + + 2010-01-14: Simon Goldschmidt + * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback + by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback() + + 2010-01-13: Simon Goldschmidt + * mem.c: The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + (patch #6966 and bug #26133) + + 2010-01-10: Simon Goldschmidt (Bill Auerbach) + * opt.h, memp.c: patch #6822 (Add option to place memory pools in + separate arrays) + + 2010-01-10: Simon Goldschmidt + * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define + LWIP_RAND() for lwip-wide randomization (to be defined in cc.h) + + 2009-12-31: Simon Goldschmidt + * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h + added timers.c/.h: Separated timer implementation from semaphore/mbox + implementation, moved timer implementation to timers.c/.h, timers are + now only called from tcpip_thread or by explicitly checking them. + (TASK#7235) + + 2009-12-27: Simon Goldschmidt + * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option + LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE) + + + ++ Bugfixes: + + 2011-04-20: Simon Goldschmidt + * sys_arch.txt: sys_arch_timeouts() is not needed any more. + + 2011-04-13: Simon Goldschmidt + * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by + using ports in the IANA private/dynamic range (49152 through 65535). + + 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl: + * etharp.h/.c: Fixed broken VLAN support. + + 2011-03-27: Simon Goldschmidt + * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp + pcbs) by checking if the pcb was bound (local_port != 0). + + 2011-03-27: Simon Goldschmidt + * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice) + + 2011-03-27: Simon Goldschmidt + * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and + raw pcbs with LWIP_TCPIP_CORE_LOCKING==1. + + 2011-03-27: Simon Goldschmidt + * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route + is present never times out) by starting retransmission timer before checking + route. + + 2011-03-22: Simon Goldschmidt + * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only + calling sio_read_abort() if the file descriptor is valid. + + 2011-03-14: Simon Goldschmidt + * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect + more than once can render a socket useless) since it mainly involves changing + "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal. + + 2011-03-13: Simon Goldschmidt + * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing + err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN: + use EALRADY instead of -1 + + 2011-03-13: Simon Goldschmidt + * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the + connection has been aborted by err_tcp (since this is not a normal closing + procedure). + + 2011-03-13: Simon Goldschmidt + * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind + with pcb->state != CLOSED + + 2011-02-17: Simon Goldschmidt + * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in + documentation + + 2011-02-17: Simon Goldschmidt + * many files: Added missing U/UL modifiers to fix 16-bit-arch portability. + + 2011-01-24: Simon Goldschmidt + * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems + + 2010-12-02: Simon Goldschmidt + * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal. + + 2010-11-23: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for + LWIP_SO_RCVBUF and ioctl/FIONREAD. + + 2010-11-23: Simon Goldschmidt + * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at + least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet. + + 2010-11-23: Simon Goldschmidt + * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after + refusing 'refused_data' again. + + 2010-11-22: Simon Goldschmidt + * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS + after a successful nonblocking connection. + + 2010-11-22: Simon Goldschmidt + * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr + must be sent link-local + + 2010-11-22: Simon Goldschmidt + * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for + LWIP_TIMERS==0 + + 2010-11-20: Simon Goldschmidt + * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number + + 2010-11-20: Simon Goldschmidt + * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to + resemble other stacks. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else + no-copy TCP writes will never succeed. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does + not match documentation: return ERR_ARG instead of ERR_VAL if not + initialized or wrong argument. + + 2010-10-20: Simon Goldschmidt + * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16 + + 2010-10-05: Simon Goldschmidt + * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when + replugging the network cable after an AutoIP address was assigned. + + 2010-08-10: Simon Goldschmidt + * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs + + 2010-08-03: Simon Goldschmidt + * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625) + + 2010-08-01: Simon Goldschmidt (patch by Greg Renda) + * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big + endian architectures) + + 2010-07-28: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP + disabled. + + 2010-07-27: Simon Goldschmidt + * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no + harm but never did anything + + 2010-07-21: Simon Goldschmidt + * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not + add IP options) + + 2010-07-16: Kieran Mansley + * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator + + 2010-07-10: Simon Goldschmidt + * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options + + 2010-06-30: Simon Goldschmidt + * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in + netconn_delete) + + 2010-06-28: Kieran Mansley + * timers.c remove unportable printing of C function pointers + + 2010-06-24: Simon Goldschmidt + * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag + NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading + + 2010-06-24: Simon Goldschmidt + * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly + implemented shutdown at socket level. + + 2010-06-21: Simon Goldschmidt + * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has + problems with zero-copy DMA MACs) by adding custom pbufs and implementing + custom pbufs that reference other (original) pbufs. Additionally set + IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side. + + 2010-06-15: Simon Goldschmidt + * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses + + 2010-06-14: Simon Goldschmidt + * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses + + 2010-06-12: Simon Goldschmidt + * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop + state + + 2010-05-17: Simon Goldschmidt + * netdb.c: Correctly NULL-terminate h_addr_list + + 2010-05-16: Simon Goldschmidt + * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent + "symbol already defined" i.e. when linking to winsock + + 2010-05-05: Simon Goldschmidt + * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may + overflow) + + 2010-04-21: Simon Goldschmidt + * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening + connection) + + 2010-03-28: Luca Ceresoli + * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers + + 2010-03-27: Luca Ceresoli + * mib2.c: patch #7130: remove meaningless const qualifiers + + 2010-03-26: Simon Goldschmidt + * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too + + 2010-03-26: Simon Goldschmidt + * various files: Fixed compiling with different options disabled (TCP/UDP), + triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled + + 2010-03-25: Simon Goldschmidt + * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly + + 2010-03-25: Simon Goldschmidt + * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side + overrunning our rcv_wnd in ooseq case. + + 2010-03-22: Simon Goldschmidt + * tcp.c: tcp_listen() did not copy the pcb's prio. + + 2010-03-19: Simon Goldschmidt + * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set + + 2010-03-14: Simon Goldschmidt + * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports + where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h + and basing PBUF_LINK_HLEN on it. + + 2010-03-08: Simon Goldschmidt + * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections + when assiging routable address): when checking incoming packets and + aborting existing connection on address change, filter out link-local + addresses. + + 2010-03-06: Simon Goldschmidt + * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING + + 2010-03-06: Simon Goldschmidt + * ipv4/ip.c: Don't try to forward link-local addresses + + 2010-03-06: Simon Goldschmidt + * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal- + addresses to gw + + 2010-03-05: Simon Goldschmidt + * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type + and state. + + 2010-03-05: Simon Goldschmidt + * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split + into multiple calls to tcp_write. + + 2010-02-21: Simon Goldschmidt + * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep + the implementation of DNS_USES_STATIC_BUF==1) + + 2010-02-20: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement + close() vs. shutdown(). Now the application does not get any more + recv callbacks after calling tcp_close(). Added tcp_shutdown(). + + 2010-02-19: Simon Goldschmidt + * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent + confusion with realloc() + + 2010-02-15: Simon Goldschmidt/Stephane Lesage + * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK + (fixes bug #28899) + + 2010-02-14: Simon Goldschmidt + * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with + LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and + admin-status of a netif are up + + 2010-02-14: Simon Goldschmidt + * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet + reception and is not really necessary + + 2010-02-14: Simon Goldschmidt + * etharp.c/.h: Fixed ARP input processing: only add a new entry if a + request was directed as us (RFC 826, Packet Reception), otherwise + only update existing entries; internalized some functions + + 2010-02-14: Simon Goldschmidt + * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be + disabled on netif used for PPPoE) by adding a new netif flag + (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet + device but prevents usage of ARP (so that ethernet_input can be used + for PPPoE). + + 2010-02-12: Simon Goldschmidt + * netif.c: netif_set_link_up/down: only do something if the link state + actually changes + + 2010-02-12: Simon Goldschmidt/Stephane Lesage + * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking + connect) + + 2010-02-12: Simon Goldschmidt + * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h) + + 2010-02-09: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110 + (recv() makes receive window update for data that wasn't received by + application) + + 2010-02-09: Simon Goldschmidt/Stephane Lesage + * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out + or any netconn_recv() error) + + 2010-02-09: Simon Goldschmidt + * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets) + + 2010-02-09: Simon Goldschmidt + * netif.c: For loopback packets, adjust the stats- and snmp-counters + for the loopback netif. + + 2010-02-08: Simon Goldschmidt + * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity + since they are not used anywhere else. + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats + (patch from bug #28798) + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and + another bug when LWIP_RAND() returns zero. + + 2010-02-04: Simon Goldschmidt + * nearly every file: Use macros defined in ip_addr.h (some of them new) + to work with IP addresses (preparation for bug #27352 - Change ip_addr + from struct to typedef (u32_t) - and better code). + + 2010-01-31: Simon Goldschmidt + * netif.c: Don't call the link-callback from netif_set_up/down() since + this invalidly retriggers DHCP. + + 2010-01-29: Simon Goldschmidt + * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the + portability file inet.h and its contents from the stack: moved htonX- + functions to def.h (and the new def.c - they are not ipv4 dependent), + let inet.h depend on ip_addr.h and not the other way round. + This fixes bug #28732. + + 2010-01-28: Kieran Mansley + * tcp.c: Ensure ssthresh >= 2*MSS + + 2010-01-27: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv + callback can lead to accessing unallocated memory. As a consequence, + ERR_ABRT means the application has called tcp_abort()! + + 2010-01-25: Simon Goldschmidt + * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY + not implemented in SNMP): write-only or not-accessible are still + returned by getnext (though not by get) + + 2010-01-24: Simon Goldschmidt + * snmp: Renamed the private mib node from 'private' to 'mib_private' to + not use reserved C/C++ keywords + + 2010-01-23: Simon Goldschmidt + * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less + than 1 ms + + 2010-01-21: Simon Goldschmidt + * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called + if tcp_enqueue fails) both in raw- and netconn-API + + 2010-01-19: Simon Goldschmidt + * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp + + 2010-01-18: Iordan Neshev/Simon Goldschmidt + * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some + bugfix backports from 2.4.x. + + 2010-01-18: Simon Goldschmidt + * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong + + 2010-01-17: Simon Goldschmidt + * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c): + task #10102: "netconn: clean up conn->err threading issues" by adding + error return value to struct api_msg_msg + + 2010-01-17: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept() + to return err_t (bugs #27709 and #28087) + + 2010-01-14: Simon Goldschmidt + * ...: Use typedef for function prototypes throughout the stack. + + 2010-01-13: Simon Goldschmidt + * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive + window = 0) by correctly draining recvmbox/acceptmbox + + 2010-01-11: Simon Goldschmidt + * pap.c: Fixed bug #13315 (PPP PAP authentication can result in + erroneous callbacks) by copying the code from recent pppd + + 2010-01-10: Simon Goldschmidt + * raw.c: Fixed bug #28506 (raw_bind should filter received packets) + + 2010-01-10: Simon Goldschmidt + * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)()) + + 2010-01-08: Simon Goldschmidt + * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535) + + 2010-01-08: Simon Goldschmidt + * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string + passed to dns_local_addhost() might be volatile + + 2010-01-07: Simon Goldschmidt + * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too + + 2010-01-06: Simon Goldschmidt + * netdb.h: Fixed bug #28496: missing include guards in netdb.h + + 2009-12-31: Simon Goldschmidt + * many ppp files: Reorganised PPP source code from ucip structure to pppd + structure to easily compare our code against the pppd code (around v2.3.1) + + 2009-12-27: Simon Goldschmidt + * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted + unit test + + +(STABLE-1.3.2) + + ++ New features: + + 2009-10-27 Simon Goldschmidt/Stephan Lesage + * netifapi.c/.h: Added netifapi_netif_set_addr() + + 2009-10-07 Simon Goldschmidt/Fabian Koch + * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to + support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO) + + 2009-08-26 Simon Goldschmidt/Simon Kallweit + * slipif.c/.h: bug #26397: SLIP polling support + + 2009-08-25 Simon Goldschmidt + * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN), + New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK. + + 2009-08-25 Simon Goldschmidt + * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*) + + 2009-08-24 Jakob Stoklund Olesen + * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond + to netif_set_link_up(). + + 2009-08-23 Simon Goldschmidt + * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state + to a human-readable string. + + ++ Bugfixes: + + 2009-12-24: Kieran Mansley + * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing + (BUG#28241) + + 2009-12-06: Simon Goldschmidt + * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can + be statically allocated (like in ucip) + + 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev) + * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT + + 2009-12-03: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit + could have non-zero length + + 2009-12-02: Simon Goldschmidt + * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting + tcp_input_pcb until after calling the pcb's callbacks + + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + + 2009-11-29: Simon Goldschmidt + * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by + queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty + + 2009-11-26: Simon Goldschmidt + * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending + segment + + 2009-11-26: Simon Goldschmidt + * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle + algorithm at PCB level + + 2009-11-22: Simon Goldschmidt + * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent + + 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach) + * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when + reusing time-wait pcb + + 2009-11-20: Simon Goldschmidt (patch by Albert Bartel) + * sockets.c: Fixed bug #28062: Data received directly after accepting + does not wake up select + + 2009-11-11: Simon Goldschmidt + * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo) + + 2009-10-30: Simon Goldschmidt + * opt.h: Increased default value for TCP_MSS to 536, updated default + value for TCP_WND to 4*TCP_MSS to keep delayed ACK working. + + 2009-10-28: Kieran Mansley + * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code + to follow algorithm from TCP/IP Illustrated + + 2009-10-27: Kieran Mansley + * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK + + 2009-10-25: Simon Goldschmidt + * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if + pcb->recv is NULL to keep rcv_wnd correct) + + 2009-10-25: Simon Goldschmidt + * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state + + 2009-10-23: Simon Goldschmidt (David Empson) + * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes + + 2009-10-21: Simon Goldschmidt + * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and + trailing 1 byte len (SYN/FIN) + + 2009-10-21: Simon Goldschmidt + * tcp_out.c: Fixed bug #27315: zero window probe and FIN + + 2009-10-19: Simon Goldschmidt + * dhcp.c/.h: Minor code simplification (don't store received pbuf, change + conditional code to assert where applicable), check pbuf length before + testing for valid reply + + 2009-10-19: Simon Goldschmidt + * dhcp.c: Removed most calls to udp_connect since they aren't necessary + when using udp_sendto_if() - always stay connected to IP_ADDR_ANY. + + 2009-10-16: Simon Goldschmidt + * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop + valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is + enabled + + 2009-10-15: Simon Goldschmidt (Oleg Tyshev) + * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit + + 2009-10-15: Simon Goldschmidt + * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv() + timeout + + 2009-10-15: Simon Goldschmidt + * autoip.c: Fixed bug #27704: autoip starts with wrong address + LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead + of network byte order + + 2009-10-11 Simon Goldschmidt (Jörg Kesten) + * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments + which are not consecutive when retransmitting unacked segments + + 2009-10-09 Simon Goldschmidt + * opt.h: Fixed default values of some stats to only be enabled if used + Fixes bug #27338: sys_stats is defined when NO_SYS = 1 + + 2009-08-30 Simon Goldschmidt + * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK + function" by checking for loopback before calling ip_frag + + 2009-08-25 Simon Goldschmidt + * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0 + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27078: Possible memory leak in pppInit() + + 2009-08-23 Simon Goldschmidt + * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result + is error. + + 2009-08-23 Simon Goldschmidt + * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF + Fixed wrong parenthesis, added check in init.c + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms + + 2009-08-23 Simon Goldschmidt + * many ppp files: bug #27267: Added include to string.h where needed + + 2009-08-23 Simon Goldschmidt + * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian) + + +(STABLE-1.3.1) + + ++ New features: + + 2009-05-10 Simon Goldschmidt + * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option + LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only + one pbuf to help MACs that don't support scatter-gather DMA. + + 2009-05-09 Simon Goldschmidt + * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming + ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + + 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen + * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive + extended info about the currently received packet. + + 2009-04-27 Simon Goldschmidt + * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1 + + 2009-04-25 Simon Goldschmidt + * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next + bigger malloc pool if one is empty (only usable with MEM_USE_POOLS). + + 2009-04-21 Simon Goldschmidt + * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static + hosts table. New configuration options DNS_LOCAL_HOSTLIST and + DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined + as an external function for lookup. + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for + TCP timestamp options, off by default. Rework tcp_enqueue() to + take option flags rather than specified option data + + 2009-02-18 Simon Goldschmidt + * cc.h: Added printf formatter for size_t: SZT_F + + 2009-02-16 Simon Goldschmidt (patch by Rishi Khan) + * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast + pings + + 2009-02-12 Simon Goldschmidt + * init.h: Added LWIP_VERSION to get the current version of the stack + + 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler) + * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead + of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc + is otherwise used) + + 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach) + * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial() + is only used by UDPLITE at present, so conditionalise it. + + 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli) + * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP + "seed" address. This should reduce AUTOIP conflicts if + LWIP_AUTOIP_CREATE_SEED_ADDR is overridden. + + 2008-10-02 Jonathan Larmour and Rishi Khan + * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking + socket. + + 2008-06-30 Simon Goldschmidt + * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from + interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows + mem_free to run between mem_malloc iterations. Added illegal counter for + mem stats. + + 2008-06-27 Simon Goldschmidt + * stats.h/.c, some other files: patch #6483: stats module improvement: + Added defines to display each module's statistic individually, added stats + defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. + + 2008-06-17 Simon Goldschmidt + * err.h: patch #6459: Made err_t overridable to use a more efficient type + (define LWIP_ERR_T in cc.h) + + 2008-06-17 Simon Goldschmidt + * slipif.c: patch #6480: Added a configuration option for slipif for symmetry + to loopif + + 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) + * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly + modified version of patch # 6370: Moved loopif code to netif.c so that + loopback traffic is supported on all netifs (all local IPs). + Added option to limit loopback packets for each netifs. + + + ++ Bugfixes: + 2009-08-12 Kieran Mansley + * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when + out of window or out of order properly + + 2009-08-12 Kieran Mansley + * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1 + + 2009-07-28 Simon Goldschmidt + * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s + + 2009-07-27 Kieran Mansley + * api.h api_msg.h netdb.h sockets.h: add missing #include directives + + 2009-07-09 Kieran Mansley + * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for + recv_avail and don't increment counters until message successfully + sent to mbox + + 2009-06-25 Kieran Mansley + * api_msg.c api.h: BUG26722: initialise netconn write variables + in netconn_alloc + + 2009-06-25 Kieran Mansley + * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set + + 2009-06-25 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct + simultaneous close behaviour, and make snd_nxt have the same meaning + as in the RFCs. + + 2009-05-12 Simon Goldschmidt + * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on + arp_table / uses etharp_query" by adding etharp_gratuitous() + + 2009-05-12 Simon Goldschmidt + * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options + to the IP header (used by igmp_ip_output_if) + + 2009-05-06 Simon Goldschmidt + * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if + defined) for SWAP_BYTES_IN_WORD to speed up checksumming. + + 2009-05-05 Simon Goldschmidt + * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select() + to crash + + 2009-05-04 Simon Goldschmidt + * init.c: snmp was not initialized in lwip_init() + + 2009-05-04 Frédéric Bernon + * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled. + + 2009-05-03 Simon Goldschmidt + * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full + (and unsent->next == NULL) + + 2009-05-02 Simon Goldschmidt + * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after + 1.3.0 in CVS only) - fixes compilation of ppp_oe.c + + 2009-05-02 Simon Goldschmidt + * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields + + 2009-05-01 Simon Goldschmidt + * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets + + 2009-05-01 Simon Goldschmidt + * ppp.c: bug #24228: Memory corruption with PPP and DHCP + + 2009-04-29 Frédéric Bernon + * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the + SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception + of broadcast packets even when this option wasn't set. Port maintainers + which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h. + If you want this option also filter broadcast on recv operations, you also + have to set IP_SOF_BROADCAST_RECV=1 in opt.h. + + 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen + * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and + DHCP/AUTOIP cooperation + + 2009-04-25 Simon Goldschmidt, Oleg Tyshev + * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd + Fixed by sorting the unsent and unacked queues (segments are inserted at the + right place in tcp_output and tcp_rexmit). + + 2009-04-25 Simon Goldschmidt + * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation + when debugging": memp_sizes contained the wrong sizes (including sanity + regions); memp pools for MEM_USE_POOLS were too small + + 2009-04-24 Simon Goldschmidt, Frédéric Bernon + * inet.c: patch #6765: Fix a small problem with the last changes (incorrect + behavior, with with ip address string not ended by a '\0', a space or a + end of line) + + 2009-04-19 Simon Goldschmidt + * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails, + pcb->err is called, not pcb->connected (with an error code). + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with + no-copy-tcpwrite": deallocate option data, only concat segments with same flags + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated + in the header pbuf, not the data pbuf) + + 2009-04-18 Simon Goldschmidt + * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore() + + 2009-04-15 Simon Goldschmidt + * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp + + 2009-04-15 Simon Goldschmidt + * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in + + 2009-04-15 Simon Goldschmidt + * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function + ip_hinted_output() (for smaller code mainly) + + 2009-04-15 Simon Goldschmidt + * inet.c: patch #6765: Supporting new line characters in inet_aton() + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option; + Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu + is big enough in dhcp_start + + 2009-04-15 Simon Goldschmidt + * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak + + 2009-04-15 Simon Goldschmidt + * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY + + 2009-04-15 Simon Goldschmidt + * sockets.c: bug #26121: set_errno can be overridden + + 2009-04-09 Kieran Mansley (patch from Luca Ceresoli ) + * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when + LWIP_TCP==0 + + 2009-04-09 Kieran Mansley (patch from Roy Lee ) + * tcp.h: Patch#6802 Add do-while-clauses to those function like + macros in tcp.h + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window + updates are calculated and sent (BUG20515) + + * tcp_in.c: cope with SYN packets received during established states, + and retransmission of initial SYN. + + * tcp_out.c: set push bit correctly when tcp segments are merged + + 2009-03-27 Kieran Mansley + * tcp_out.c set window correctly on probes (correcting change made + yesterday) + + 2009-03-26 Kieran Mansley + * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping + connections where no reset required (bug #25622) + + * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes + (bug #20779) + + 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach) + * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be + too small depending on MEM_ALIGNMENT + + 2009-02-16 Simon Goldschmidt + * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard; + converted size argument of netconn_write to 'size_t' + + 2009-02-16 Simon Goldschmidt + * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host + by moving accept callback function pointer to TCP_PCB_COMMON + + 2009-02-12 Simon Goldschmidt + * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size" + option) + + 2009-02-11 Simon Goldschmidt + * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start) + + 2009-02-11 Simon Goldschmidt + * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize: + RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv()) + + 2009-02-10 Simon Goldschmidt + * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD: + Accepts_pending is decrease on a corresponding listen pcb when a connection + in state SYN_RCVD is close. + + 2009-01-28 Jonathan Larmour + * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run + out of pool pbufs. + + 2008-12-19 Simon Goldschmidt + * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 + + 2008-12-10 Tamas Somogyi, Frédéric Bernon + * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and + port uses deleted netbuf. + + 2008-10-18 Simon Goldschmidt + * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length + in tcp_parseopt + + 2008-10-15 Simon Goldschmidt + * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers + by packing the struct ip_reass_helper. + + 2008-10-03 David Woodhouse, Jonathan Larmour + * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address. + + 2008-10-02 Jonathan Larmour + * dns.c: Hard-code structure sizes, to avoid issues on some compilers where + padding is included. + + 2008-09-30 Jonathan Larmour + * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an + assertion check that addrlen isn't NULL. + + 2008-09-30 Jonathan Larmour + * tcp.c: Fix bug #24227, wrong error message in tcp_bind. + + 2008-08-26 Simon Goldschmidt + * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and + inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h + + 2008-08-14 Simon Goldschmidt + * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when + tcp_close returns != ERR_OK) + + 2008-07-08 Frédéric Bernon + * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters + in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). + + 2008-06-24 Jonathan Larmour + * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused + if tcp_seg_copy fails. + + 2008-06-17 Simon Goldschmidt + * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) + and created defines for swapping bytes and folding u32 to u16. + + 2008-05-30 Kieran Mansley + * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd + rather than rcv_ann_wnd when deciding if packets are in-window. + Contributed by + + 2008-05-30 Kieran Mansley + * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow + passing as function pointers when MEM_LIBC_MALLOC is defined. + + 2008-05-09 Jonathan Larmour + * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to + stop it being treated as a fatal error. + + 2008-04-15 Simon Goldschmidt + * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP + (flag now cleared) + + 2008-03-27 Simon Goldschmidt + * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free + from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 + in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs + or heap memory from interrupt context + + 2008-03-26 Simon Goldschmidt + * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote + host sent a zero mss as TCP option. + + +(STABLE-1.3.0) + + ++ New features: + + 2008-03-10 Jonathan Larmour + * inet_chksum.c: Allow choice of one of the sample algorithms to be + made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM. + + 2008-01-22 Frédéric Bernon + * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in + TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names. + + 2008-01-14 Frédéric Bernon + * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable + to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the + tcp_recv callback (see rawapi.txt). + + 2008-01-14 Frédéric Bernon, Marc Chaland + * ip.c: Integrate patch #6369" ip_input : checking before realloc". + + 2008-01-12 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::sem per netconn::op_completed like suggested for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-12 Frédéric Bernon + * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE, + DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues + sizes), like suggested for the task #7490 "Add return value to sys_mbox_post". + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490 + "Add return value to sys_mbox_post". tcpip_callback is always defined as + "blocking" ("block" parameter = 1). + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-05 Frédéric Bernon + * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: + Introduce changes for task #7490 "Add return value to sys_mbox_post" with some + modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which + indicate the number of pointers query by the mailbox. There is three defines + in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the + netconn::acceptmbox. Port maintainers, you can decide to just add this new + parameter in your implementation, but to ignore it to keep the previous behavior. + The new sys_mbox_trypost function return a value to know if the mailbox is + full or if the message is posted. Take a look to sys_arch.txt for more details. + This new function is used in tcpip_input (so, can be called in an interrupt + context since the function is not blocking), and in recv_udp and recv_raw. + + 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c, + tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the + "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add + documentation in the rawapi.txt file. + + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer + + 2007-12-31 Frédéric Bernon, Luca Ceresoli + * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets + in autoip". The change in etharp_raw could be removed, since all calls to + etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be + wrong in the future. + + 2007-12-30 Frédéric Bernon, Tom Evans + * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address + Filtering" reported by Tom Evans. + + 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c, + sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API + applications have to call 'tcp_accepted(pcb)' in their accept callback to + keep accepting new connections. + + 2007-12-13 Frédéric Bernon + * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result" + by err_t type. Add a new err_t code "ERR_INPROGRESS". + + 2007-12-12 Frédéric Bernon + * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles + are the one which have ram usage. + + 2007-12-05 Frédéric Bernon + * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static + set of variables (=0) or a local one (=1). In this last case, your port should + provide a function "struct hostent* sys_thread_hostent( struct hostent* h)" + which have to do a copy of "h" and return a pointer ont the "per-thread" copy. + + 2007-12-03 Simon Goldschmidt + * ip.c: ip_input: check if a packet is for inp first before checking all other + netifs on netif_list (speeds up packet receiving in most cases) + + 2007-11-30 Simon Goldschmidt + * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access + UDP: move a (connected) pcb selected for input to the front of the list of + pcbs so that it is found faster next time. Same for RAW pcbs that have eaten + a packet. + + 2007-11-28 Simon Goldschmidt + * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS + + 2007-11-25 Simon Goldschmidt + * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy + algorithm. + + 2007-11-24 Simon Goldschmidt + * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c + to the new file netdb.c; included lwip_getaddrinfo. + + 2007-11-21 Simon Goldschmidt + * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss + based on the MTU of the netif used to send. Enabled by default. Disable by + setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492. + + 2007-11-19 Frédéric Bernon + * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name + received match the name query), implement DNS_USES_STATIC_BUF (the place where + copy dns payload to parse the response), return an error if there is no place + for a new query, and fix some minor problems. + + 2007-11-16 Simon Goldschmidt + * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c + removed files: core/inet.c, core/inet6.c + Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into + inet and chksum part; changed includes in all lwIP files as appropriate + + 2007-11-16 Simon Goldschmidt + * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential + dns resolver function for netconn api (netconn_gethostbyname) and socket api + (gethostbyname/gethostbyname_r). + + 2007-11-15 Jim Pettinato, Frédéric Bernon + * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name + requests with RAW api interface. Initialization is done in lwip_init() with + build time options. DNS timer is added in tcpip_thread context. DHCP can set + DNS server ip addresses when options are received. You need to set LWIP_DNS=1 + in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get + some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo" + list with points to improve. + + 2007-11-06 Simon Goldschmidt + * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly + enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status + for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined. + + 2007-11-06 Simon Goldschmidt + * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include + core header files in api.h (ip/tcp/udp/raw.h) to hide the internal + implementation from netconn api applications. + + 2007-11-03 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP & + RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled + by default). Netconn API users can use the netconn_recv_bufsize macro to access + it. This is a first release which have to be improve for TCP. Note it used the + netconn::recv_avail which need to be more "thread-safe" (note there is already + the problem for FIONREAD with lwip_ioctl/ioctlsocket). + + 2007-11-01 Frédéric Bernon, Marc Chaland + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c: + Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api + layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api + layer. This option enable to delayed TCP PUSH flag on multiple "write" calls. + Note that previous "copy" parameter for "write" APIs is now called "apiflags". + + 2007-10-24 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than + TCP_EVENT_xxx macros to get a code more readable. It could also help to remove + some code (like we have talk in "patch #5919 : Create compile switch to remove + select code"), but it could be done later. + + 2007-10-08 Simon Goldschmidt + * many files: Changed initialization: many init functions are not needed any + more since we now rely on the compiler initializing global and static + variables to zero! + + 2007-10-06 Simon Goldschmidt + * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY + to enqueue the received pbufs so that multiple packets can be reassembled + simultaneously and no static reassembly buffer is needed. + + 2007-10-05 Simon Goldschmidt + * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so + all netifs (or ports) can use it. + + 2007-10-05 Frédéric Bernon + * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the + common function to reduce a little bit the footprint (for all functions using + only the "netif" parameter). + + 2007-10-03 Frédéric Bernon + * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down, + netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce + a little bit the footprint (for all functions using only the "netif" parameter). + + 2007-09-15 Frédéric Bernon + * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF + option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for + netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for + IP_MULTICAST_TTL and IP_MULTICAST_IF. + + 2007-09-10 Frédéric Bernon + * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles + even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime() + each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can + decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but + call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime() + or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. + This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside + snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only + when it's queried (any direct call to "sysuptime" is changed by a call to + snmp_get_sysuptime). + + 2007-09-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP, + and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags + if you want IGMP on an interface. igmp_stop() is now called inside netif_remove(). + igmp_report_groups() is now called inside netif_set_link_up() (need to have + LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait + the next query message to receive the matching multicast streams). + + 2007-09-08 Frédéric Bernon + * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains + IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change). + Use this new field to access to common pcb fields (ttl, tos, so_options, etc...). + Enable to access to these fields with LWIP_TCP=0. + + 2007-09-05 Frédéric Bernon + * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h, + ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option + LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default). + Be careful, disabling ICMP make your product non-compliant to RFC1122, but + help to reduce footprint, and to reduce "visibility" on the Internet. + + 2007-09-05 Frédéric Bernon, Bill Florac + * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list + for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new + parameters have to be provided: a task name, and a task stack size. For this + one, since it's platform dependant, you could define the best one for you in + your lwipopts.h. For port maintainers, you can just add these new parameters + in your sys_arch.c file, and but it's not mandatory, use them in your OS + specific functions. + + 2007-09-05 Frédéric Bernon + * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings + inside init.c for task #7142 "Sanity check user-configurable values". + + 2007-09-04 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by + memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the + value). It will avoid potential fragmentation problems, use a counter to know + how many times a group is used on an netif, and free it when all applications + leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity + check if LWIP_IGMP!=0). + + 2007-09-03 Frédéric Bernon + * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement". + Initialize igmp_mac_filter to NULL in netif_add (this field should be set in + the netif's "init" function). Use the "imr_interface" field (for socket layer) + and/or the "interface" field (for netconn layer), for join/leave operations. + The igmp_join/leavegroup first parameter change from a netif to an ipaddr. + This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany). + + 2007-08-30 Frédéric Bernon + * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions + from api/api_lib". Now netbuf API is independant of netconn, and can be used + with other API (application based on raw API, or future "socket2" API). Ports + maintainers just have to add src/api/netbuf.c in their makefile/projects. + + 2007-08-30 Frédéric Bernon, Jonathan Larmour + * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check + user-configurable values". + + 2007-08-29 Frédéric Bernon + * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start. + igmp_start is call inside netif_add. Now, igmp initialization is in the same + spirit than the others modules. Modify some IGMP debug traces. + + 2007-08-29 Frédéric Bernon + * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function" + Add lwip_init function to regroup all modules initializations, and to provide + a place to add code for task #7142 "Sanity check user-configurable values". + Ports maintainers should remove direct initializations calls from their code, + and add init.c in their makefiles. Note that lwip_init() function is called + inside tcpip_init, but can also be used by raw api users since all calls are + disabled when matching options are disabled. Also note that their is new options + in opt.h, you should configure in your lwipopts.h (they are enabled per default). + + 2007-08-26 Marc Boucher + * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL + since they can under certain circumstances be called with an invalid conn + pointer after the connection has been closed (and conn has been freed). + + 2007-08-25 Frédéric Bernon (Artem Migaev's Patch) + * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up". + Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set. + + 2007-08-22 Frédéric Bernon + * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK + to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release. + + 2007-08-22 Frédéric Bernon + * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT & + ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the + name is tcpip_input (we keep the name of 1.2.0 function). + + 2007-08-17 Jared Grubb + * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool + settings into new memp_std.h and optional user file lwippools.h. This adds + more dynamic mempools, and allows the user to create an arbitrary number of + mempools for mem_malloc. + + 2007-08-16 Marc Boucher + * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function; + otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely + close the connection. + + 2007-08-16 Marc Boucher + * sockets.c: lwip_accept(): check netconn_peer() error return. + + 2007-08-16 Marc Boucher + * mem.c, mem.h: Added mem_calloc(). + + 2007-08-16 Marc Boucher + * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT) + for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG + and starving other message types. + Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API + + 2007-08-16 Marc Boucher + * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf + type and flgs (later renamed to flags). + Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*. + Improved lwip_recvfrom(). TCP push now propagated. + + 2007-08-16 Marc Boucher + * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global + provided by etharp. + + 2007-08-16 Marc Boucher + * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h, + etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c: + Added PPPoE support and various PPP improvements. + + 2007-07-25 Simon Goldschmidt + * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial, + making netbuf_copy_partial use this function. + + 2007-07-25 Simon Goldschmidt + * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with + 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and + other stacks. + + 2007-07-13 Jared Grubb (integrated by Frédéric Bernon) + * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add + a link callback in the netif struct, and functions to handle it. Be carefull + for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c) + if you want to be sure to be compatible with future changes... + + 2007-06-30 Frédéric Bernon + * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions. + + 2007-06-21 Simon Goldschmidt + * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both + LWIP_AUTOIP =0 and =1 to remove redundant code. + + 2007-06-21 Simon Goldschmidt + * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option + MEM_USE_POOLS to use 4 pools with different sized elements instead of a + heap. This both prevents memory fragmentation and gives a higher speed + at the cost of more memory consumption. Turned off by default. + + 2007-06-21 Simon Goldschmidt + * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of + netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into + int to be able to send a bigger buffer than 64K with one time (mainly + used from lwip_send). + + 2007-06-21 Simon Goldschmidt + * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write + into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too. + + 2007-06-21 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in + netconn_write from api_lib.c to api_msg.c to also prevent multiple context- + changes on low memory or empty send-buffer. + + 2007-06-18 Simon Goldschmidt + * etharp.c, etharp.h: Changed etharp to use a defined hardware address length + of 6 to avoid loading netif->hwaddr_len every time (since this file is only + used for ethernet and struct eth_addr already had a defined length of 6). + + 2007-06-17 Simon Goldschmidt + * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets + to disable UDP checksum generation on transmit. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid + pointers or parameters, and let the possibility to redefined it in cc.h. Use + this macro to check "conn" parameter in api_msg.c functions. + + 2007-06-11 Simon Goldschmidt + * sockets.c, sockets.h: Added UDP lite support for sockets + + 2007-06-10 Simon Goldschmidt + * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled + by default) to switch off UDP-Lite support if not needed (reduces udp.c code + size) + + 2007-06-09 Dominik Spies (integrated by Frédéric Bernon) + * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h: + AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and + LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt + (see TODO mark in the source code). + + 2007-06-09 Simon Goldschmidt + * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for + etharp_output() to match netif->output so etharp_output() can be used + directly as netif->output to save one function call. + + 2007-06-08 Simon Goldschmidt + * netif.h, ethernetif.c, slipif.c, loopif.c: Added define + NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables, + added initialization of those to ethernetif, slipif and loopif. + + 2007-05-18 Simon Goldschmidt + * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF + (defaulting to off for now) that can be set to 0 to send fragmented + packets by passing PBUF_REFs down the stack. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP + connections, such present in patch #5959. + + 2007-05-23 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx + code in only one part... + + 2007-05-18 Simon Goldschmidt + * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp + elements to overflow. This is achieved by adding some bytes before and after + each pool element (increasing their size, of course), filling them with a + prominent value and checking them on freeing the element. + Set it to 2 to also check every element in every pool each time memp_malloc() + or memp_free() is called (slower but more helpful). + + 2007-05-10 Simon Goldschmidt + * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for + PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce + code size. + + 2007-05-11 Frédéric Bernon + * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c: + Include a function pointer instead of a table index in the message to reduce + footprint. Disable some part of lwip_send and lwip_sendto if some options are + not set (LWIP_TCP, LWIP_UDP, LWIP_RAW). + + 2007-05-10 Simon Goldschmidt + * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus + \ extern "C" {' in all header files. Now you can write your application using + the lwIP stack in C++ and simply #include the core files. Note I have left + out the netif/ppp/*h header files for now, since I don't know which files are + included by applications and which are for internal use only. + + 2007-05-09 Simon Goldschmidt + * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library + memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for + situations where some compilers might inline the copy and save a function + call. Also replaced all calls to memcpy() with calls to (S)MEMCPY(). + + 2007-05-08 Simon Goldschmidt + * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc()) + to be overriden in case the C-library malloc implementation is not protected + against concurrent access. + + 2007-05-04 Simon Goldschmidt (Atte Kojo) + * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending + multiple packets to the same host. + + 2007-05-04 Frédéric Bernon, Jonathan Larmour + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible + to corrupt remote addr/port connection state". Reduce problems "not enought memory" with + netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between + sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function. + Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct, + these fields are now renamed "addr" & "port". + + 2007-04-11 Jonathan Larmour + * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new + sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return + with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro + by the port in sys_arch.h if desired. + + 2007-04-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API + allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp + clients, using new functions from netifapi.h. Disable as default (no port change to do). + + 2007-04-05 Frédéric Bernon + * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant. + + 2007-04-04 Simon Goldschmidt + * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x) + use this for and architecture-independent form to tell the compiler you intentionally + are not using this variable. Can be overriden in cc.h. + + 2007-03-28 Frédéric Bernon + * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to + define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded + string, point on one of your's ethernetif field, or alloc a string you will free yourself). + It will be used by DHCP to register a client hostname, but can also be use when you call + snmp_set_sysname. + + 2007-03-28 Frédéric Bernon + * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to + initialize a network interface's flag with. It tell this interface is an ethernet + device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility + Support for IPv4" section 4.6) when interface is "up" with netif_set_up(). + + 2007-03-26 Frédéric Bernon, Jonathan Larmour + * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build + time if you only use PPP or SLIP. The default is enable. Note we don't have to call + etharp_init in your port's initilization sequence if you use tcpip.c, because this call + is done in tcpip_init function. + + 2007-03-22 Frédéric Bernon + * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the + new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in + your lwipopts.h. More, unused counters are not defined in the stats structs, and not + display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined + but never used. Fix msg_in.c with the correct #if test for a stat display. + + 2007-03-21 Kieran Mansley + * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). + Provides callback on netif up/down state change. + + 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c, + ip.c, netif.h, tcpip.c, opt.h: + New configuration option LWIP_IGMP to enable IGMP processing. Based on only one + filter per all network interfaces. Declare a new function in netif to enable to + control the MAC filter (to reduce lwIP traffic processing). + + 2007-03-11 Frédéric Bernon + * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can + be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this + unless you know what you're doing (default are RFC1122 compliant). Note + that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds. + + 2007-03-08 Frédéric Bernon + * tcp.h: Keepalive values can be configured at compile time, but don't change + this unless you know what you're doing (default are RFC1122 compliant). + + 2007-03-08 Frédéric Bernon + * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h: + Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO + on UDP sockets/netconn. + + 2007-03-08 Simon Goldschmidt + * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time. + + 2007-03-06 Frédéric Bernon + * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: + Implement SO_RCVTIMEO on UDP sockets/netconn. + + 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt) + * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated + on the stack and remove the API msg type from memp + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * sockets.h, sockets.c: Move socket initialization to new + lwip_socket_init() function. + NOTE: this changes the API with ports. Ports will have to be + updated to call lwip_socket_init() now. + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * api_lib.c: Use memcpy in netbuf_copy_partial. + + + ++ Bug fixes: + + 2008-03-17 Frédéric Bernon, Ed Kerekes + * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have + some problems to fill the IP header on some targets, use now the + ip.h macros to do it). + + 2008-03-13 Frédéric Bernon + * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using + (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a + TCP connection caused a crash. Note that using (lwip_)recvfrom + like this is a bit slow and that using (lwip)getpeername is the + good lwip way to do it (so, using recv is faster on tcp sockets). + + 2008-03-12 Frédéric Bernon, Jonathan Larmour + * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's + recv_raw() does not consume data", and the ping sample (with + LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom + returned the IP payload, without the IP header). + + 2008-03-04 Jonathan Larmour + * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors + and/or warnings on some systems where mem_size_t and size_t differ. + * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc. + + 2008-03-04 Kieran Mansley (contributions by others) + * Numerous small compiler error/warning fixes from contributions to + mailing list after 1.3.0 release candidate made. + + 2008-01-25 Cui hengbin (integrated by Frédéric Bernon) + * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures. + + 2008-01-15 Kieran Mansley + * tcp_out.c: BUG20511. Modify persist timer to start when we are + prevented from sending by a small send window, not just a zero + send window. + + 2008-01-09 Jonathan Larmour + * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid + conflict with Linux system headers. + + 2008-01-06 Jonathan Larmour + * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP + address entirely on receiving a DHCPNAK, and restarting discovery. + + 2007-12-21 Simon Goldschmidt + * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail + is not protected" by using new macros for interlocked access to modify/test + netconn->recv_avail. + + 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev) + * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state) + + 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling + of silly window avoidance and prevent lwIP from shrinking the window) + + 2007-12-04 Simon Goldschmidt + * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last + data packet was lost): add assert that all segment lists are empty in + tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED + state from LAST_ACK in tcp_process + + 2007-12-02 Simon Goldschmidt + * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET + If including for system-struct timeval, LWIP_TIMEVAL_PRIVATE now + has to be set to 0 in lwipopts.h + + 2007-12-02 Simon Goldschmidt + * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always + allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen + netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox. + This is a fix for thread-safety and allocates all items needed for a netconn + when the netconn is created. + + 2007-11-30 Simon Goldschmidt + * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple + netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed + to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same + port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address) + + 2007-11-27 Simon Goldschmidt + * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by + letting ip_route only use netifs that are up. + + 2007-11-27 Simon Goldschmidt + * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF + and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and + sockets block most operations once they have seen a fatal error. + + 2007-11-27 Simon Goldschmidt + * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the + netif to send as an argument (to be able to send on netifs that are down). + + 2007-11-26 Simon Goldschmidt + * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs + arrive out-of-order + + 2007-11-21 Simon Goldschmidt + * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early + Fixed the nagle algorithm; nagle now also works for all raw API applications + and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY' + + 2007-11-12 Frédéric Bernon + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most + of the netconn_peer and netconn_addr processing is done inside tcpip_thread + context in do_getaddr. + + 2007-11-10 Simon Goldschmidt + * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can + happen any time). Now the packet simply isn't enqueued when out of memory. + + 2007-11-01 Simon Goldschmidt + * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or + TCP_MSS if that is smaller) as long as no MSS option is received from the + remote host. + + 2007-11-01 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN) + is now based on TCP_MSS instead of pcb->mss (on passive open now effectively + sending our configured TCP_MSS instead of the one received). + + 2007-11-01 Simon Goldschmidt + * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was + calculated based on the configured TCP_MSS, not on the MSS option received + with SYN+ACK. + + 2007-10-09 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too + short and also was generated wrong if checksum coverage != tot_len; + receive: checksum was calculated wrong if checksum coverage != tot_len + + 2007-10-08 Simon Goldschmidt + * mem.c: lfree was not updated in mem_realloc! + + 2007-10-07 Frédéric Bernon + * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential + crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: + this change cause an API breakage for netconn_addr, since a parameter + type change. Any compiler should cause an error without any changes in + yours netconn_peer calls (so, it can't be a "silent change"). It also + reduce a little bit the footprint for socket layer (lwip_getpeername & + lwip_getsockname use now a common lwip_getaddrname function since + netconn_peer & netconn_addr have the same parameters). + + 2007-09-20 Simon Goldschmidt + * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state) + by checking tcp_tw_pcbs also + + 2007-09-19 Simon Goldschmidt + * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies) + + 2007-09-15 Mike Kleshov + * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used) + + 2007-09-06 Frédéric Bernon + * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove + it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which + already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h" + if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h. + + 2007-08-30 Frédéric Bernon + * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, + and fix some coding style. + + 2007-08-28 Frédéric Bernon + * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any + kind of packets. These packets are considered like Ethernet packets (payload + pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets + are considered like IP packets (payload pointing to iphdr). + + 2007-08-27 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error + problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state + and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT). + + 2007-08-24 Kieran Mansley + * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy + compiler (Paradigm C++) + + 2007-08-09 Frédéric Bernon, Bill Florac + * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement. + Introduce IGMP_STATS to centralize statistics management. + + 2007-08-09 Frédéric Bernon, Bill Florac + * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast + packet on a udp pcb binded on an netif's IP address, and not on "any". + + 2007-08-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement. + This is mainly on using lookup/lookfor, and some coding styles... + + 2007-07-26 Frédéric Bernon (and "thedoctor") + * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages. + + 2007-07-25 Simon Goldschmidt + * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if + tcp_output fails in tcp_close, the code in do_close_internal gets simpler + (tcp_output is called again later from tcp timers). + + 2007-07-25 Simon Goldschmidt + * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old + copy_from_pbuf, which illegally modified the given pbuf. + + 2007-07-25 Simon Goldschmidt + * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs: + changed snd_queuelen++ to snd_queuelen += pbuf_clen(p). + + 2007-07-24 Simon Goldschmidt + * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the + correct state (must be CLOSED). + + 2007-07-13 Thomas Taranowski (commited by Jared Grubb) + * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed + allocation. It now returns NULL. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in + all error cases. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed, + because current code doesn't follow rawapi.txt documentation. + + 2007-07-13 Kieran Mansley + * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in + out of sequence processing of received packets + + 2007-07-03 Simon Goldschmidt + * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an + assumption is made that this pbuf is in one piece (i.e. not chained). These + assumptions clash with the possibility of converting to fully pool-based + pbuf implementations, where PBUF_RAM pbufs might be chained. + + 2007-07-03 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems + when closing tcp netconns: removed conn->sem, less context switches when + closing, both netconn_close and netconn_delete should safely close tcp + connections. + + 2007-07-02 Simon Goldschmidt + * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c, + tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off) + to cache ARP table indices with each pcb instead of single-entry cache for + the complete stack. + + 2007-07-02 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent + warnings when assigning to smaller types. + + 2007-06-28 Simon Goldschmidt + * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing. + + 2007-06-28 Simon Goldschmidt + * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if + a segment contained chained pbufs) + + 2007-06-28 Frédéric Bernon + * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute + a "pseudo-random" value based on netif's MAC and some autoip fields. It's always + possible to define this macro in your own lwipopts.h to always use C library's + rand(). Note that autoip_create_rand_addr doesn't use this macro. + + 2007-06-28 Frédéric Bernon + * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option + LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications + in api_lib/api_msg (use pointers and not type with table, etc...) + + 2007-06-26 Simon Goldschmidt + * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload + for udp packets with no matching pcb. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match + could get udp input packets if the remote side matched. + + 2007-06-13 Simon Goldschmidt + * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get + changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0. + + 2007-06-13 Simon Goldschmidt + * api_msg.c: pcb_new sets conn->err if protocol is not implemented + -> netconn_new_..() does not allocate a new connection for unsupported + protocols. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * api_lib.c: change return expression in netconn_addr and netconn_peer, because + conn->err was reset to ERR_OK without any reasons (and error was lost)... + + 2007-06-13 Frédéric Bernon, Matthias Weisser + * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename + MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid + some macro names collision with some OS macros. + + 2007-06-11 Simon Goldschmidt + * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0, + create checksum over the complete packet. On RX, if it's < 8 (and not 0), + discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both + UDP & UDP Lite. + + 2007-06-11 Srinivas Gollakota & Oleg Tyshev + * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags" + where TCP flags wasn't initialized in tcp_keepalive. + + 2007-06-03 Simon Goldschmidt + * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function + registered, p->payload was modified without modifying p->len if sending + icmp_dest_unreach() (had no negative effect but was definitively wrong). + + 2007-06-03 Simon Goldschmidt + * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp + re-used the input pbuf even if that didn't have enough space to include the + link headers. Now the space is tested and a new pbuf is allocated for the + echo response packet if the echo request pbuf isn't big enough. + + 2007-06-01 Simon Goldschmidt + * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread. + + 2007-05-23 Frédéric Bernon + * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only + allocated by do_listen if success) and netconn_accept errors handling. In + most of api_lib functions, we replace some errors checkings like "if (conn==NULL)" + by ASSERT, except for netconn_delete. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return + an error code if it's impossible to fetch a pbuf on a TCP connection (and not + directly close the recvmbox). + + 2007-05-22 Simon Goldschmidt + * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of + bound but unconnected (and non-listening) tcp_pcbs. + + 2007-05-22 Frédéric Bernon + * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only + used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of + sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features + like "sys_timeout" in their application threads. + + 2007-05-22 Frédéric Bernon + * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see + which parameters are used by which do_xxx function, and to avoid "misusing" + parameters (patch #5938). + + 2007-05-22 Simon Goldschmidt + * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938: + changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto + is only 8 bits wide. This affects the api, as there, the protocol was + u16_t, too. + + 2007-05-18 Simon Goldschmidt + * memp.c: addition to patch #5913: smaller pointer was returned but + memp_memory was the same size -> did not save memory. + + 2007-05-16 Simon Goldschmidt + * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns + != ERR_OK. + + 2007-05-16 Simon Goldschmidt + * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same + as the one of the netif used for sending to prevent sending from old + addresses after a netif address gets changed (partly fixes bug #3168). + + 2007-05-16 Frédéric Bernon + * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work + with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in + tcpip_init) because we have to be sure that network interfaces are already + added (mac filter is updated only in igmp_init for the moment). + + 2007-05-16 Simon Goldschmidt + * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls + into sys_arch_sem_wait calls to prevent timers from running while waiting + for the heap. This fixes bug #19167. + + 2007-05-13 Simon Goldschmidt + * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines + for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from + tcp.h to sockets.h. + + 2007-05-07 Simon Goldschmidt + * mem.c: Another attempt to fix bug #17922. + + 2007-05-04 Simon Goldschmidt + * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy() + implementation so that it can be reused (don't allocate the target + pbuf inside pbuf_copy()). + + 2007-05-04 Simon Goldschmidt + * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem + to save a little RAM (next pointer of memp is not used while not in pool). + + 2007-05-03 "maq" + * sockets.c: Fix ioctl FIONREAD when some data remains from last recv. + (patch #3574). + + 2007-04-23 Simon Goldschmidt + * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results + in NULL reference for incoming TCP packets". Loopif has to be configured + (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input() + (multithreading environments, e.g. netif->input() = tcpip_input()) or + putting packets on a list that is fed to the stack by calling loopif_poll() + (single-thread / NO_SYS / polling environment where e.g. + netif->input() = ip_input). + + 2007-04-17 Jonathan Larmour + * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold + the difference between two u16_t's. + * sockets.h: FD_SETSIZE needs to match number of sockets, which is + MEMP_NUM_NETCONN in sockets.c right now. + + 2007-04-12 Jonathan Larmour + * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580). + + 2007-04-12 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission + timer is reset to fix bug#19434, with help from Oleg Tyshev. + + 2007-04-11 Simon Goldschmidt + * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than + previously thought need to be copied (everything but PBUF_ROM!). Cleaned up + pbuf.c: removed functions no needed any more (by etharp). + + 2007-04-11 Kieran Mansley + * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix + "Constant is long" warnings with 16bit compilers. Contributed by + avatar@mmlab.cse.yzu.edu.tw + + 2007-04-05 Frédéric Bernon, Jonathan Larmour + * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on + the mailbox is active". Now, the post is only done during a connect, and do_send, + do_write and do_join_leave_group don't do anything if a previous error was signaled. + + 2007-04-03 Frédéric Bernon + * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output + packets. See patch #5834. + + 2007-03-30 Frédéric Bernon + * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add + missing pcb allocations checking (in do_bind, and for each raw_new). Fix style. + + 2007-03-30 Frédéric Bernon + * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with + others environment defines (these were too "generic"). + + 2007-03-28 Frédéric Bernon + * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call + result and can cause a crash. lwip_send now check netbuf_ref result. + + 2007-03-28 Simon Goldschmidt + * sockets.c Remove "#include " from sockets.c to avoid multiple + definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is + defined. This is the way it should have been already (looking at + doc/sys_arch.txt) + + 2007-03-28 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS + + IP and TCP headers *and* physical link headers + + 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov) + * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause + to send some garbage. It is not a definitive solution, but the patch does solve + the problem for most cases. + + 2007-03-22 Frédéric Bernon + * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used). + + 2007-03-22 Frédéric Bernon + * api_lib.c: somes resources couldn't be freed if there was errors during + netconn_new_with_proto_and_callback. + + 2007-03-22 Frédéric Bernon + * ethernetif.c: update netif->input calls to check return value. In older ports, + it's a good idea to upgrade them, even if before, there could be another problem + (access to an uninitialized mailbox). + + 2007-03-21 Simon Goldschmidt + * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed + by casting to unsigned). + + 2007-03-21 Frédéric Bernon + * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from + api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a + dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call. + Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a + faster and more reliable communication between api_lib and tcpip. + + 2007-03-21 Frédéric Bernon + * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0. + + 2007-03-21 Frédéric Bernon + * api_msg.c, igmp.c, igmp.h: Fix C++ style comments + + 2007-03-21 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS + + IP and TCP headers + + 2007-03-21 Kieran Mansley + * Fix all uses of pbuf_header to check the return value. In some + cases just assert if it fails as I'm not sure how to fix them, but + this is no worse than before when they would carry on regardless + of the failure. + + 2007-03-21 Kieran Mansley + * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and + comment out missing header include in icmp.c + + 2007-03-20 Frédéric Bernon + * memp.h, stats.c: Fix stats_display function where memp_names table wasn't + synchronized with memp.h. + + 2007-03-20 Frédéric Bernon + * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input, + tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with + network interfaces. Also fix a compiler warning. + + 2007-03-20 Kieran Mansley + * udp.c: Only try and use pbuf_header() to make space for headers if + not a ROM or REF pbuf. + + 2007-03-19 Frédéric Bernon + * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg() + and api_msg_post(). + + 2007-03-19 Frédéric Bernon + * Remove unimplemented "memp_realloc" function from memp.h. + + 2007-03-11 Simon Goldschmidt + * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused + memory corruption. + + 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov) + * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251 + (missing `const' qualifier in socket functions), to get more compatible to + standard POSIX sockets. + + 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov) + * sockets.c: Add asserts inside bind, connect and sendto to check input + parameters. Remove excessive set_errno() calls after get_socket(), because + errno is set inside of get_socket(). Move last sock_set_errno() inside + lwip_close. + + 2007-03-09 Simon Goldschmidt + * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory + was allocated too small. + + 2007-03-06 Simon Goldschmidt + * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect + the stack from concurrent access. + + 2007-03-06 Frédéric Bernon, Dmitry Potapov + * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy + call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input(). + + 2007-03-06 Simon Goldschmidt + * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files + if IP_FRAG == 0 and IP_REASSEMBLY == 0 + + 2007-03-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration + option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput. + Allow to do ARP processing for incoming packets inside tcpip_thread + (protecting ARP layer against concurrent access). You can also disable + old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0. + Older ports have to use tcpip_ethinput. + + 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov) + * err.h, err.c: fixed compiler warning "initialization dircards qualifiers + from pointer target type" + + 2007-03-05 Frédéric Bernon + * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES, + ETHARP_TRUST_IP_MAC, review SO_REUSE) + + 2007-03-04 Frédéric Bernon + * api_msg.c: Remove some compiler warnings : parameter "pcb" was never + referenced. + + 2007-03-04 Frédéric Bernon + * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from + Dmitry Potapov). + The api_msg struct stay on the stack (not moved to netconn struct). + + 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov) + * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if + SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available) + Also fixed cast warning in pbuf_alloc() + + 2007-03-04 Simon Goldschmidt + * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt + existing pbuf chain when enqueuing multiple pbufs to a pending ARP request + + 2007-03-03 Frédéric Bernon + * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;" + It is static, and never used in udp.c except udp_init(). + + 2007-03-02 Simon Goldschmidt + * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from + tcpip_thread() to tcpip_init(). This way, raw API connections can be + initialized before tcpip_thread is running (e.g. before OS is started) + + 2007-03-02 Frédéric Bernon + * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call + interval. + + 2007-02-28 Kieran Mansley + * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved + outside the region of the pbuf by pbuf_header() + + 2007-02-28 Kieran Mansley + * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero + when supplied timeout is also non-zero + +(STABLE-1.2.0) + + 2006-12-05 Leon Woestenberg + * CHANGELOG: Mention STABLE-1.2.0 release. + + ++ New features: + + 2006-12-01 Christiaan Simons + * mem.h, opt.h: Added MEM_LIBC_MALLOC option. + Note this is a workaround. Currently I have no other options left. + + 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour) + * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define + to include/lwip/opt.h. + * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL. + Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h. + * opt.h: Add above new options. + + 2006-08-18 Christiaan Simons + * tcp_{in,out}.c: added SNMP counters. + * ipv4/ip.c: added SNMP counters. + * ipv4/ip_frag.c: added SNMP counters. + + 2006-08-08 Christiaan Simons + * etharp.{c,h}: added etharp_find_addr() to read + (stable) ethernet/IP address pair from ARP table + + 2006-07-14 Christiaan Simons + * mib_structs.c: added + * include/lwip/snmp_structs.h: added + * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct + + 2006-07-06 Christiaan Simons + * snmp/asn1_{enc,dec}.c added + * snmp/mib2.c added + * snmp/msg_{in,out}.c added + * include/lwip/snmp_asn1.h added + * include/lwip/snmp_msg.h added + * doc/snmp_agent.txt added + + 2006-03-29 Christiaan Simons + * inet.c, inet.h: Added platform byteswap support. + Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and + optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros. + + ++ Bug fixes: + + 2006-11-30 Christiaan Simons + * dhcp.c: Fixed false triggers of request_timeout. + + 2006-11-28 Christiaan Simons + * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags. + + 2006-10-11 Christiaan Simons + * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h: + Partially accepted patch #5449 for ANSI C compatibility / build fixes. + * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol + identifier from 170 to 136 (bug #17574). + + 2006-10-10 Christiaan Simons + * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice. + + 2006-08-17 Christiaan Simons + * udp.c: Fixed bug #17200, added check for broadcast + destinations for PCBs bound to a unicast address. + + 2006-08-07 Christiaan Simons + * api_msg.c: Flushing TCP output in do_close() (bug #15926). + + 2006-06-27 Christiaan Simons + * api_msg.c: Applied patch for cold case (bug #11135). + In accept_function() ensure newconn->callback is always initialized. + + 2006-06-15 Christiaan Simons + * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748), + facilitate printing of mem_size_t and u16_t statistics. + + 2006-06-14 Christiaan Simons + * api_msg.c: Applied patch #5146 to handle allocation failures + in accept() by Kevin Lawson. + + 2006-05-26 Christiaan Simons + * api_lib.c: Removed conn->sem creation and destruction + from netconn_write() and added sys_sem_new to netconn_new_*. + +(STABLE-1_1_1) + + 2006-03-03 Christiaan Simons + * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap + access and added pbuf_alloc() return value checks. + + 2006-01-01 Leon Woestenberg + * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is + now handled by the checksum routine properly. + + 2006-02-27 Leon Woestenberg + * pbuf.c: Fix alignment; pbuf_init() would not work unless + pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.) + + 2005-12-20 Leon Woestenberg + * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch + submitted by Mitrani Hiroshi. + + 2005-12-15 Christiaan Simons + * inet.c: Disabled the added summing routine to preserve code space. + + 2005-12-14 Leon Woestenberg + * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson. + Added Curt McDowell's optimized checksumming routine for future + inclusion. Need to create test case for unaliged, aligned, odd, + even length combination of cases on various endianess machines. + + 2005-12-09 Christiaan Simons + * inet.c: Rewrote standard checksum routine in proper portable C. + + 2005-11-25 Christiaan Simons + * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only. + * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t, + u32_t, s32_t typedefs. This solves most debug word-length assumes. + + 2005-07-17 Leon Woestenberg + * inet.c: Fixed unaligned 16-bit access in the standard checksum + routine by Peter Jolasson. + * slipif.c: Fixed implementation assumption of single-pbuf datagrams. + + 2005-02-04 Leon Woestenberg + * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch. + * tcp_{out|in}.c: Applied patch fixing unaligned access. + + 2005-01-04 Leon Woestenberg + * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement. + + 2005-01-03 Leon Woestenberg + * udp.c: UDP pcb->recv() was called even when it was NULL. + +(STABLE-1_1_0) + + 2004-12-28 Leon Woestenberg + * etharp.*: Disabled multiple packets on the ARP queue. + This clashes with TCP queueing. + + 2004-11-28 Leon Woestenberg + * etharp.*: Fixed race condition from ARP request to ARP timeout. + Halved the ARP period, doubled the period counts. + ETHARP_MAX_PENDING now should be at least 2. This prevents + the counter from reaching 0 right away (which would allow + too little time for ARP responses to be received). + + 2004-11-25 Leon Woestenberg + * dhcp.c: Decline messages were not multicast but unicast. + * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD. + Do not try hard to insert arbitrary packet's source address, + etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. + etharp_query() now always DOES call ETHARP_TRY_HARD so that users + querying an address will see it appear in the cache (DHCP could + suffer from this when a server invalidly gave an in-use address.) + * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are + comparing network addresses (identifiers), not the network masks + themselves. + * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given + IP address actually belongs to the network of the given interface. + + 2004-11-24 Kieran Mansley + * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state. + +(STABLE-1_1_0-RC1) + + 2004-10-16 Kieran Mansley + * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately, + even if one is already pending, if the rcv_wnd is above a threshold + (currently TCP_WND/2). This avoids waiting for a timer to expire to send a + delayed ACK in order to open the window if the stack is only receiving data. + + 2004-09-12 Kieran Mansley + * tcp*.*: Retransmit time-out handling improvement by Sam Jansen. + + 2004-08-20 Tony Mountifield + * etharp.c: Make sure the first pbuf queued on an ARP entry + is properly ref counted. + + 2004-07-27 Tony Mountifield + * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler + warnings about comparison. + * pbuf.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. Closed an unclosed comment. + * tcp.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. + * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons(). + * inet.c: Added a couple of casts to quiet the compiler. + No need to test isascii(c) before isdigit(c) or isxdigit(c). + + 2004-07-22 Tony Mountifield + * inet.c: Made data types consistent in inet_ntoa(). + Added casts for return values of checksum routines, to pacify compiler. + * ip_frag.c, tcp_out.c, sockets.c, pbuf.c + Small corrections to some debugging statements, to pacify compiler. + + 2004-07-21 Tony Mountifield + * etharp.c: Removed spurious semicolon and added missing end-of-comment. + * ethernetif.c Updated low_level_output() to match prototype for + netif->linkoutput and changed low_level_input() similarly for consistency. + * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype + of raw_recv() in raw.h and so avoid compiler error. + * sockets.c: Added trivial (int) cast to keep compiler happier. + * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros. + +(STABLE-1_0_0) + + ++ Changes: + + 2004-07-05 Leon Woestenberg + * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure + your cc.h file defines this either 1 or 0. If non-defined, + defaults to 1. + * .c: Added and includes where used. + * etharp.c: Made some array indices unsigned. + + 2004-06-27 Leon Woestenberg + * netif.*: Added netif_set_up()/down(). + * dhcp.c: Changes to restart program flow. + + 2004-05-07 Leon Woestenberg + * etharp.c: In find_entry(), instead of a list traversal per candidate, do a + single-pass lookup for different candidates. Should exploit locality. + + 2004-04-29 Leon Woestenberg + * tcp*.c: Cleaned up source comment documentation for Doxygen processing. + * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC. + * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by + the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option. + + ++ Bug fixes: + + 2004-04-27 Leon Woestenberg + * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution + suggested by Timmy Brolin. Fix for 32-bit processors that cannot access + non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix + is to prefix the 14-bit Ethernet headers with two padding bytes. + + 2004-04-23 Leon Woestenberg + * ip_addr.c: Fix in the ip_addr_isbroadcast() check. + * etharp.c: Fixed the case where the packet that initiates the ARP request + is not queued, and gets lost. Fixed the case where the packets destination + address is already known; we now always queue the packet and perform an ARP + request. + +(STABLE-0_7_0) + + ++ Bug fixes: + + * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition. + * Fixed TCP bug in dequeueing of FIN from out of order segment queue. + * Fixed two possible NULL references in rare cases. + +(STABLE-0_6_6) + + ++ Bug fixes: + + * Fixed DHCP which did not include the IP address in DECLINE messages. + + ++ Changes: + + * etharp.c has been hauled over a bit. + +(STABLE-0_6_5) + + ++ Bug fixes: + + * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic. + * Packets sent from ARP queue had invalid source hardware address. + + ++ Changes: + + * Pass-by ARP requests do now update the cache. + + ++ New features: + + * No longer dependent on ctype.h. + * New socket options. + * Raw IP pcb support. + +(STABLE-0_6_4) + + ++ Bug fixes: + + * Some debug formatters and casts fixed. + * Numereous fixes in PPP. + + ++ Changes: + + * DEBUGF now is LWIP_DEBUGF + * pbuf_dechain() has been re-enabled. + * Mentioned the changed use of CVS branches in README. + +(STABLE-0_6_3) + + ++ Bug fixes: + + * Fixed pool pbuf memory leak in pbuf_alloc(). + Occured if not enough PBUF_POOL pbufs for a packet pbuf chain. + Reported by Savin Zlobec. + + * PBUF_POOL chains had their tot_len field not set for non-first + pbufs. Fixed in pbuf_alloc(). + + ++ New features: + + * Added PPP stack contributed by Marc Boucher + + ++ Changes: + + * Now drops short packets for ICMP/UDP/TCP protocols. More robust. + + * ARP queueuing now queues the latest packet instead of the first. + This is the RFC recommended behaviour, but can be overridden in + lwipopts.h. + +(0.6.2) + + ++ Bugfixes: + + * TCP has been fixed to deal with the new use of the pbuf->ref + counter. + + * DHCP dhcp_inform() crash bug fixed. + + ++ Changes: + + * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed + pbuf_refresh(). This has sped up pbuf pool operations considerably. + Implemented by David Haas. + +(0.6.1) + + ++ New features: + + * The packet buffer implementation has been enhanced to support + zero-copy and copy-on-demand for packet buffers which have their + payloads in application-managed memory. + Implemented by David Haas. + + Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy + if an outgoing packet can be directly sent on the link, or perform + a copy-on-demand when necessary. + + The application can safely assume the packet is sent, and the RAM + is available to the application directly after calling udp_send() + or similar function. + + ++ Bugfixes: + + * ARP_QUEUEING should now correctly work for all cases, including + PBUF_REF. + Implemented by Leon Woestenberg. + + ++ Changes: + + * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer + to a '0.0.0.0' IP address. + + * The packet buffer implementation is changed. The pbuf->ref counter + meaning has changed, and several pbuf functions have been + adapted accordingly. + + * netif drivers have to be changed to set the hardware address length field + that must be initialized correctly by the driver (hint: 6 for Ethernet MAC). + See the contrib/ports/c16x cs8900 driver as a driver example. + + * netif's have a dhcp field that must be initialized to NULL by the driver. + See the contrib/ports/c16x cs8900 driver as a driver example. + +(0.5.x) This file has been unmaintained up to 0.6.1. All changes are + logged in CVS but have not been explained here. + +(0.5.3) Changes since version 0.5.2 + + ++ Bugfixes: + + * memp_malloc(MEMP_API_MSG) could fail with multiple application + threads because it wasn't protected by semaphores. + + ++ Other changes: + + * struct ip_addr now packed. + + * The name of the time variable in arp.c has been changed to ctime + to avoid conflicts with the time() function. + +(0.5.2) Changes since version 0.5.1 + + ++ New features: + + * A new TCP function, tcp_tmr(), now handles both TCP timers. + + ++ Bugfixes: + + * A bug in tcp_parseopt() could cause the stack to hang because of a + malformed TCP option. + + * The address of new connections in the accept() function in the BSD + socket library was not handled correctly. + + * pbuf_dechain() did not update the ->tot_len field of the tail. + + * Aborted TCP connections were not handled correctly in all + situations. + + ++ Other changes: + + * All protocol header structs are now packed. + + * The ->len field in the tcp_seg structure now counts the actual + amount of data, and does not add one for SYN and FIN segments. + +(0.5.1) Changes since version 0.5.0 + + ++ New features: + + * Possible to run as a user process under Linux. + + * Preliminary support for cross platform packed structs. + + * ARP timer now implemented. + + ++ Bugfixes: + + * TCP output queue length was badly initialized when opening + connections. + + * TCP delayed ACKs were not sent correctly. + + * Explicit initialization of BSS segment variables. + + * read() in BSD socket library could drop data. + + * Problems with memory alignment. + + * Situations when all TCP buffers were used could lead to + starvation. + + * TCP MSS option wasn't parsed correctly. + + * Problems with UDP checksum calculation. + + * IP multicast address tests had endianess problems. + + * ARP requests had wrong destination hardware address. + + ++ Other changes: + + * struct eth_addr changed from u16_t[3] array to u8_t[6]. + + * A ->linkoutput() member was added to struct netif. + + * TCP and UDP ->dest_* struct members where changed to ->remote_*. + + * ntoh* macros are now null definitions for big endian CPUs. + +(0.5.0) Changes since version 0.4.2 + + ++ New features: + + * Redesigned operating system emulation layer to make porting easier. + + * Better control over TCP output buffers. + + * Documenation added. + + ++ Bugfixes: + + * Locking issues in buffer management. + + * Bugfixes in the sequential API. + + * IP forwarding could cause memory leakage. This has been fixed. + + ++ Other changes: + + * Directory structure somewhat changed; the core/ tree has been + collapsed. + +(0.4.2) Changes since version 0.4.1 + + ++ New features: + + * Experimental ARP implementation added. + + * Skeleton Ethernet driver added. + + * Experimental BSD socket API library added. + + ++ Bugfixes: + + * In very intense situations, memory leakage could occur. This has + been fixed. + + ++ Other changes: + + * Variables named "data" and "code" have been renamed in order to + avoid name conflicts in certain compilers. + + * Variable++ have in appliciable cases been translated to ++variable + since some compilers generate better code in the latter case. + +(0.4.1) Changes since version 0.4 + + ++ New features: + + * TCP: Connection attempts time out earlier than data + transmissions. Nagle algorithm implemented. Push flag set on the + last segment in a burst. + + * UDP: experimental support for UDP-Lite extensions. + + ++ Bugfixes: + + * TCP: out of order segments were in some cases handled incorrectly, + and this has now been fixed. Delayed acknowledgements was broken + in 0.4, has now been fixed. Binding to an address that is in use + now results in an error. Reset connections sometimes hung an + application; this has been fixed. + + * Checksum calculation sometimes failed for chained pbufs with odd + lengths. This has been fixed. + + * API: a lot of bug fixes in the API. The UDP API has been improved + and tested. Error reporting and handling has been + improved. Logical flaws and race conditions for incoming TCP + connections has been found and removed. + + * Memory manager: alignment issues. Reallocating memory sometimes + failed, this has been fixed. + + * Generic library: bcopy was flawed and has been fixed. + + ++ Other changes: + + * API: all datatypes has been changed from generic ones such as + ints, to specified ones such as u16_t. Functions that return + errors now have the correct type (err_t). + + * General: A lot of code cleaned up and debugging code removed. Many + portability issues have been fixed. + + * The license was changed; the advertising clause was removed. + + * C64 port added. + + * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri + Kosunen, Mikael Caleres, and Frits Wilmink for reporting and + fixing bugs! + +(0.4) Changes since version 0.3.1 + + * Memory management has been radically changed; instead of + allocating memory from a shared heap, memory for objects that are + rapidly allocated and deallocated is now kept in pools. Allocation + and deallocation from those memory pools is very fast. The shared + heap is still present but is used less frequently. + + * The memory, memory pool, and packet buffer subsystems now support + 4-, 2-, or 1-byte alignment. + + * "Out of memory" situations are handled in a more robust way. + + * Stack usage has been reduced. + + * Easier configuration of lwIP parameters such as memory usage, + TTLs, statistics gathering, etc. All configuration parameters are + now kept in a single header file "lwipopts.h". + + * The directory structure has been changed slightly so that all + architecture specific files are kept under the src/arch + hierarchy. + + * Error propagation has been improved, both in the protocol modules + and in the API. + + * The code for the RTXC architecture has been implemented, tested + and put to use. + + * Bugs have been found and corrected in the TCP, UDP, IP, API, and + the Internet checksum modules. + + * Bugs related to porting between a 32-bit and a 16-bit architecture + have been found and corrected. + + * The license has been changed slightly to conform more with the + original BSD license, including the advertisement clause. + +(0.3.1) Changes since version 0.3 + + * Fix of a fatal bug in the buffer management. Pbufs with allocated + RAM never returned the RAM when the pbuf was deallocated. + + * TCP congestion control, window updates and retransmissions did not + work correctly. This has now been fixed. + + * Bugfixes in the API. + +(0.3) Changes since version 0.2 + + * New and improved directory structure. All include files are now + kept in a dedicated include/ directory. + + * The API now has proper error handling. A new function, + netconn_err(), now returns an error code for the connection in + case of errors. + + * Improvements in the memory management subsystem. The system now + keeps a pointer to the lowest free memory block. A new function, + mem_malloc2() tries to allocate memory once, and if it fails tries + to free some memory and retry the allocation. + + * Much testing has been done with limited memory + configurations. lwIP now does a better job when overloaded. + + * Some bugfixes and improvements to the buffer (pbuf) subsystem. + + * Many bugfixes in the TCP code: + + - Fixed a bug in tcp_close(). + + - The TCP receive window was incorrectly closed when out of + sequence segments was received. This has been fixed. + + - Connections are now timed-out of the FIN-WAIT-2 state. + + - The initial congestion window could in some cases be too + large. This has been fixed. + + - The retransmission queue could in some cases be screwed up. This + has been fixed. + + - TCP RST flag now handled correctly. + + - Out of sequence data was in some cases never delivered to the + application. This has been fixed. + + - Retransmitted segments now contain the correct acknowledgment + number and advertised window. + + - TCP retransmission timeout backoffs are not correctly computed + (ala BSD). After a number of retransmissions, TCP now gives up + the connection. + + * TCP connections now are kept on three lists, one for active + connections, one for listening connections, and one for + connections that are in TIME-WAIT. This greatly speeds up the fast + timeout processing for sending delayed ACKs. + + * TCP now provides proper feedback to the application when a + connection has been successfully set up. + + * More comments have been added to the code. The code has also been + somewhat cleaned up. + +(0.2) Initial public release. diff --git a/app/src/main/jni/badvpn/lwip/CMakeLists.txt b/app/src/main/jni/badvpn/lwip/CMakeLists.txt new file mode 100644 index 0000000..db6c2c5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/CMakeLists.txt @@ -0,0 +1,26 @@ +add_library(lwip + src/core/timers.c + src/core/udp.c + src/core/memp.c + src/core/init.c + src/core/pbuf.c + src/core/tcp.c + src/core/tcp_out.c + src/core/sys.c + src/core/netif.c + src/core/def.c + src/core/mem.c + src/core/tcp_in.c + src/core/stats.c + src/core/inet_chksum.c + src/core/ipv4/icmp.c + src/core/ipv4/ip4.c + src/core/ipv4/ip4_addr.c + src/core/ipv4/ip_frag.c + src/core/ipv6/ip6.c + src/core/ipv6/nd6.c + src/core/ipv6/icmp6.c + src/core/ipv6/ip6_addr.c + src/core/ipv6/ip6_frag.c + custom/sys.c +) diff --git a/app/src/main/jni/badvpn/lwip/COPYING b/app/src/main/jni/badvpn/lwip/COPYING new file mode 100644 index 0000000..e23898b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + diff --git a/app/src/main/jni/badvpn/lwip/FILES b/app/src/main/jni/badvpn/lwip/FILES new file mode 100644 index 0000000..6625319 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/FILES @@ -0,0 +1,4 @@ +src/ - The source code for the lwIP TCP/IP stack. +doc/ - The documentation for lwIP. + +See also the FILES file in each subdirectory. diff --git a/app/src/main/jni/badvpn/lwip/README b/app/src/main/jni/badvpn/lwip/README new file mode 100644 index 0000000..a62cc4f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/README @@ -0,0 +1,89 @@ +INTRODUCTION + +lwIP is a small independent implementation of the TCP/IP protocol +suite that has been developed by Adam Dunkels at the Computer and +Networks Architectures (CNA) lab at the Swedish Institute of Computer +Science (SICS). + +The focus of the lwIP TCP/IP implementation is to reduce the RAM usage +while still having a full scale TCP. This making lwIP suitable for use +in embedded systems with tens of kilobytes of free RAM and room for +around 40 kilobytes of code ROM. + +FEATURES + + * IP (Internet Protocol) including packet forwarding over multiple network + interfaces + * ICMP (Internet Control Message Protocol) for network maintenance and debugging + * IGMP (Internet Group Management Protocol) for multicast traffic management + * UDP (User Datagram Protocol) including experimental UDP-lite extensions + * TCP (Transmission Control Protocol) with congestion control, RTT estimation + and fast recovery/fast retransmit + * Specialized raw/native API for enhanced performance + * Optional Berkeley-like socket API + * DNS (Domain names resolver) + * SNMP (Simple Network Management Protocol) + * DHCP (Dynamic Host Configuration Protocol) + * AUTOIP (for IPv4, conform with RFC 3927) + * PPP (Point-to-Point Protocol) + * ARP (Address Resolution Protocol) for Ethernet + +LICENSE + +lwIP is freely available under a BSD license. + +DEVELOPMENT + +lwIP has grown into an excellent TCP/IP stack for embedded devices, +and developers using the stack often submit bug fixes, improvements, +and additions to the stack to further increase its usefulness. + +Development of lwIP is hosted on Savannah, a central point for +software development, maintenance and distribution. Everyone can +help improve lwIP by use of Savannah's interface, CVS and the +mailing list. A core team of developers will commit changes to the +CVS source tree. + +The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and +contributions (such as platform ports) are in the 'contrib' module. + +See doc/savannah.txt for details on CVS server access for users and +developers. + +Last night's CVS tar ball can be downloaded from: + http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING] + +The current CVS trees are web-browsable: + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/ + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/ + +Submit patches and bugs via the lwIP project page: + http://savannah.nongnu.org/projects/lwip/ + + +DOCUMENTATION + +The original out-dated homepage of lwIP and Adam Dunkels' papers on +lwIP are at the official lwIP home page: + http://www.sics.se/~adam/lwip/ + +Self documentation of the source code is regularly extracted from the +current CVS sources and is available from this web page: + http://www.nongnu.org/lwip/ + +There is now a constantly growin wiki about lwIP at + http://lwip.wikia.com/wiki/LwIP_Wiki + +Also, there are mailing lists you can subscribe at + http://savannah.nongnu.org/mail/?group=lwip +plus searchable archives: + http://lists.nongnu.org/archive/html/lwip-users/ + http://lists.nongnu.org/archive/html/lwip-devel/ + +Reading Adam's papers, the files in docs/, browsing the source code +documentation and browsing the mailing list archives is a good way to +become familiar with the design of lwIP. + +Adam Dunkels +Leon Woestenberg + diff --git a/app/src/main/jni/badvpn/lwip/UPGRADING b/app/src/main/jni/badvpn/lwip/UPGRADING new file mode 100644 index 0000000..6501107 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/UPGRADING @@ -0,0 +1,144 @@ +This file lists major changes between release versions that require +ports or applications to be changed. Use it to update a port or an +application written for an older version of lwIP to correctly work +with newer versions. + + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ Application changes: + + * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for + compatibility to old applications, but will be removed in the future). + + * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc() + + +++ Raw API: + * Changed the semantics of tcp_close() (since it was rather a + shutdown before): Now the application does *NOT* get any calls to the recv + callback (aside from NULL/closed) after calling tcp_close() + + * When calling tcp_abort() from a raw API TCP callback function, + make sure you return ERR_ABRT to prevent accessing unallocated memory. + (ERR_ABRT now means the applicaiton has called tcp_abort!) + + +++ Netconn API: + * Changed netconn_receive() and netconn_accept() to return + err_t, not a pointer to new data/netconn. + + +++ Socket API: + * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they + now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT. + + * Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + +++ all APIs: + * correctly implemented SO(F)_REUSEADDR + + ++ Port changes + + +++ new files: + + * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h: + + * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains + the actual application programmer's API + + * Separated timer implementation from sys.h/.c, moved to timers.h/.c; + Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you + still want to use your own timer implementation for NO_SYS==0 (as before). + + +++ sys layer: + + * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/ + sys_sem_t; + + * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + + * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use + binary semaphores instead of mutexes - as before) + + +++ new options: + + * Don't waste memory when chaining segments, added option TCP_OVERSIZE to + prevent creating many small pbufs when calling tcp_write with many small + blocks of data. Instead, pbufs are allocated larger than needed and the + space is used for later calls to tcp_write. + + * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs + in tcp_write/udp_send. + + * Added an additional option LWIP_ETHERNET to support ethernet without ARP + (necessary for pure PPPoE) + + * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may + be used to place these pools into user-defined memory by using external + declaration. + + * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT + + +++ new pools: + + * Netdb uses a memp pool for allocating memory when getaddrinfo() is called, + so MEMP_NUM_NETDB has to be set accordingly. + + * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so + MEMP_NUM_LOCALHOSTLIST has to be set accordingly. + + * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have + to be set accordingly. + + * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES + has to be set accordingly + + * Integrated loopif into netif.c - loopif does not have to be created by the + port any more, just define LWIP_HAVE_LOOPIF to 1. + + * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined + in cc.h, e.g. used by igmp) + + * Added printf-formatter X8_F to printf u8_t as hex + + * The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + + * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work + with user-allocated structs instead of calling mem_malloc + + * Added const char* name to mem- and memp-stats for easier debugging. + + * Calculate the TCP/UDP checksum while copying to only fetch data once: + Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum + + * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to + more than one pcb. + + * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned + off any more, if this is set to 0, only one packet (the most recent one) is + queued (like demanded by RFC 1122). + + + ++ Major bugfixes/improvements + + * Implemented tcp_shutdown() to only shut down one end of a connection + * Implemented shutdown() at socket- and netconn-level + * Added errorset support to select() + improved select speed overhead + * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x) + * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1 + * Use macros defined in ip_addr.h to work with IP addresses + * Implemented many nonblocking socket/netconn functions + * Fixed ARP input processing: only add a new entry if a request was directed as us + * mem_realloc() to mem_trim() to prevent confusion with realloc() + * Some improvements for AutoIP (don't route/forward link-local addresses, don't break + existing connections when assigning a routable address) + * Correctly handle remote side overrunning our rcv_wnd in ooseq case + * Removed packing from ip_addr_t, the packed version is now only used in protocol headers + * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0 + * Added support for static ARP table entries + +(STABLE-1.3.2) + + * initial version of this file diff --git a/app/src/main/jni/badvpn/lwip/custom/arch/cc.h b/app/src/main/jni/badvpn/lwip/custom/arch/cc.h new file mode 100644 index 0000000..653a2e2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/custom/arch/cc.h @@ -0,0 +1,96 @@ +/** + * @file cc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_CC_H +#define LWIP_CUSTOM_CC_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define s16_t int16_t +#define u32_t uint32_t +#define s32_t int32_t +#define mem_ptr_t uintptr_t + +#define PACK_STRUCT_BEGIN B_START_PACKED +#define PACK_STRUCT_END B_END_PACKED +#define PACK_STRUCT_STRUCT B_PACKED + +#define LWIP_PLATFORM_DIAG(x) { if (BLog_WouldLog(BLOG_CHANNEL_lwip, BLOG_INFO)) { BLog_Begin(); BLog_Append x; BLog_Finish(BLOG_CHANNEL_lwip, BLOG_INFO); } } +#define LWIP_PLATFORM_ASSERT(x) { fprintf(stderr, "%s: lwip assertion failure: %s\n", __FUNCTION__, (x)); abort(); } + +#define U16_F PRIu16 +#define S16_F PRId16 +#define X16_F PRIx16 +#define U32_F PRIu32 +#define S32_F PRId32 +#define X32_F PRIx32 +#define SZT_F "zu" + +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) hton16(x) +#define LWIP_PLATFORM_HTONL(x) hton32(x) + +#define LWIP_RAND() ( \ + (((uint32_t)(rand() & 0xFF)) << 24) | \ + (((uint32_t)(rand() & 0xFF)) << 16) | \ + (((uint32_t)(rand() & 0xFF)) << 8) | \ + (((uint32_t)(rand() & 0xFF)) << 0) \ +) + +// for BYTE_ORDER +#if defined(BADVPN_USE_WINAPI) && !defined(_MSC_VER) + #include +#elif defined(BADVPN_LINUX) + #include +#elif defined(BADVPN_FREEBSD) + #include +#else + #define LITTLE_ENDIAN 1234 + #define BIG_ENDIAN 4321 + #if defined(BADVPN_LITTLE_ENDIAN) + #define BYTE_ORDER LITTLE_ENDIAN + #else + #define BYTE_ORDER BIG_ENDIAN + #endif +#endif + +#endif diff --git a/app/src/main/jni/badvpn/lwip/custom/arch/perf.h b/app/src/main/jni/badvpn/lwip/custom/arch/perf.h new file mode 100644 index 0000000..09c9d47 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/custom/arch/perf.h @@ -0,0 +1,36 @@ +/** + * @file perf.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_PERF_H +#define LWIP_CUSTOM_PERF_H + +#define PERF_START +#define PERF_STOP(x) + +#endif diff --git a/app/src/main/jni/badvpn/lwip/custom/lwipopts.h b/app/src/main/jni/badvpn/lwip/custom/lwipopts.h new file mode 100644 index 0000000..64e03ec --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/custom/lwipopts.h @@ -0,0 +1,70 @@ +/** + * @file lwipopts.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_LWIPOPTS_H +#define LWIP_CUSTOM_LWIPOPTS_H + +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_ARP 0 +#define ARP_QUEUEING 0 +#define IP_FORWARD 0 +#define LWIP_ICMP 1 +#define LWIP_RAW 0 +#define LWIP_DHCP 0 +#define LWIP_AUTOIP 0 +#define LWIP_SNMP 0 +#define LWIP_IGMP 0 +#define LWIP_DNS 0 +#define LWIP_UDP 0 +#define LWIP_UDPLITE 0 +#define LWIP_TCP 1 +#define LWIP_CALLBACK_API 1 +#define LWIP_NETIF_API 0 +#define LWIP_NETIF_LOOPBACK 0 +#define LWIP_HAVE_LOOPIF 0 +#define LWIP_HAVE_SLIPIF 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define PPP_SUPPORT 0 +#define LWIP_IPV6 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_AUTOCONFIG 0 + +#define MEMP_NUM_TCP_PCB_LISTEN 16 +#define MEMP_NUM_TCP_PCB 1024 +#define TCP_MSS 1460 +#define TCP_SND_BUF 16384 +#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF)/(TCP_MSS)) + +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 + +#endif diff --git a/app/src/main/jni/badvpn/lwip/custom/sys.c b/app/src/main/jni/badvpn/lwip/custom/sys.c new file mode 100644 index 0000000..efd1455 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/custom/sys.c @@ -0,0 +1,37 @@ +/** + * @file sys.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +u32_t sys_now (void) +{ + return btime_gettime(); +} diff --git a/app/src/main/jni/badvpn/lwip/doc/FILES b/app/src/main/jni/badvpn/lwip/doc/FILES new file mode 100644 index 0000000..05d356f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/FILES @@ -0,0 +1,6 @@ +savannah.txt - How to obtain the current development source code. +contrib.txt - How to contribute to lwIP as a developer. +rawapi.txt - The documentation for the core API of lwIP. + Also provides an overview about the other APIs and multithreading. +snmp_agent.txt - The documentation for the lwIP SNMP agent. +sys_arch.txt - The documentation for a system abstraction layer of lwIP. diff --git a/app/src/main/jni/badvpn/lwip/doc/contrib.txt b/app/src/main/jni/badvpn/lwip/doc/contrib.txt new file mode 100644 index 0000000..39596fc --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/contrib.txt @@ -0,0 +1,63 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two) + can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded + as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead + for reporting a compiler warning fix. +6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/app/src/main/jni/badvpn/lwip/doc/rawapi.txt b/app/src/main/jni/badvpn/lwip/doc/rawapi.txt new file mode 100644 index 0000000..8c19030 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/rawapi.txt @@ -0,0 +1,511 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed + into the accept callback! + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The apiflags can be one or more of: + - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated + for the data to be copied into. If this flag is not given, no new memory + should be allocated and the data should only be referenced by pointer. This + also means that the memory behind dataptr must not change until the data is + ACKed by the remote host + - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given, + the PSH flag is set in the last segment created by this call to tcp_write. + If this flag is given, the PSH flag is not set. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct tcp_pcb *pcb) + + Aborts the connection by sending a RST (reset) segment to the remote + host. The pcb is deallocated. This function never fails. + + ATTENTION: When calling this from one of the TCP callbacks, make + sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + or you will risk accessing deallocated memory or memory leaks! + + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + ip_addr_t *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, ip_addr_t *ipaddr, + ip_addr_t *netmask, ip_addr_t *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. + +For more optimization hints take a look at the lwIP wiki. + +--- Zero-copy MACs + +To achieve zero-copy on transmit, the data passed to the raw API must +remain unchanged until sent. Because the send- (or write-)functions return +when the packets have been enqueued for sending, data must be kept stable +after that, too. + +This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions +must *not* be reused by the application unless their ref-count is 1. + +For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too, +but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while +PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change). + +Also, data passed to tcp_write without the copy-flag must not be changed! + +Therefore, be careful which type of PBUF you use and if you copy TCP data +or not! diff --git a/app/src/main/jni/badvpn/lwip/doc/savannah.txt b/app/src/main/jni/badvpn/lwip/doc/savannah.txt new file mode 100644 index 0000000..409905b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/app/src/main/jni/badvpn/lwip/doc/snmp_agent.txt b/app/src/main/jni/badvpn/lwip/doc/snmp_agent.txt new file mode 100644 index 0000000..2653230 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads than the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for a dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/app/src/main/jni/badvpn/lwip/doc/sys_arch.txt b/app/src/main/jni/badvpn/lwip/doc/sys_arch.txt new file mode 100644 index 0000000..847cd77 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/doc/sys_arch.txt @@ -0,0 +1,267 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that +allows both using pointers or actual OS structures to be used. This way, memory +required for such types can be either allocated in place (globally or on the +stack) or on the heap (allocated internally in the "*_new()" functions). + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- err_t sys_sem_new(sys_sem_t *sem, u8_t count) + + Creates a new semaphore. The semaphore is allocated to the memory that 'sem' + points to (which can be both a pointer or the actual OS structure). + The "count" argument specifies the initial state of the semaphore (which is + either 0 or 1). + If the semaphore has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_sem_free(sys_sem_t *sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t *sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- int sys_sem_valid(sys_sem_t *sem) + + Returns 1 if the semaphore is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_sem_set_invalid(sys_sem_t *sem) + + Invalidate a semaphore so that sys_sem_valid() returns 0. + ATTENTION: This does NOT mean that the semaphore shall be deallocated: + sys_sem_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +- err_t sys_mbox_new(sys_mbox_t *mbox, int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + If the mailbox has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_mbox_free(sys_mbox_t *mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t *mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- int sys_mbox_valid(sys_mbox_t *mbox) + + Returns 1 if the mailbox is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_mbox_set_invalid(sys_mbox_t *mbox) + + Invalidate a mailbox so that sys_mbox_valid() returns 0. + ATTENTION: This does NOT mean that the mailbox shall be deallocated: + sys_mbox_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +For some configurations, you also need: + +- u32_t sys_now(void) + + This optional function returns the current time in milliseconds (don't care + for wraparound, this is only used for time diffs). + Not implementing this function means you cannot use some modules (e.g. TCP + timestamps, internal timeouts for NO_SYS==1). + + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/app/src/main/jni/badvpn/lwip/lwip-base-version b/app/src/main/jni/badvpn/lwip/lwip-base-version new file mode 100644 index 0000000..b48d5b6 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/lwip-base-version @@ -0,0 +1 @@ +666e84eef281d0059377d0f5029c1c550488f829 diff --git a/app/src/main/jni/badvpn/lwip/src/FILES b/app/src/main/jni/badvpn/lwip/src/FILES new file mode 100644 index 0000000..952aeab --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/FILES @@ -0,0 +1,13 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here, + as well as the ARP module. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/app/src/main/jni/badvpn/lwip/src/api/api_lib.c b/app/src/main/jni/badvpn/lwip/src/api/api_lib.c new file mode 100644 index 0000000..adaaad4 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/api_lib.c @@ -0,0 +1,788 @@ +/** + * @file + * Sequential API External module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + struct api_msg msg; + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + err_t err; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err); + if (err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + sys_sem_free(&conn->op_completed); + sys_mbox_free(&conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + } + return conn; +} + +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + msg.function = lwip_netconn_do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + + netconn_free(conn); + + /* don't care for return value of lwip_netconn_do_delconn since it only calls void functions */ + + return ERR_OK; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = ip_2_ipX(addr); + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + /* The TCP version waits for the connect to succeed, + so always needs to use message passing. */ + msg.function = lwip_netconn_do_connect; + err = tcpip_apimsg(&msg); + } +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP + else +#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + { + /* UDP and RAW only set flags, so we can use core-locking. */ + TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ +#if LWIP_TCP + struct api_msg msg; + err_t err; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @param new_conn pointer where the new connection is stored + * @return ERR_OK if a new connection has been received or an error + * code otherwise + */ +err_t +netconn_accept(struct netconn *conn, struct netconn **new_conn) +{ +#if LWIP_TCP + struct netconn *newconn; + err_t err; +#if TCP_LISTEN_BACKLOG + struct api_msg msg; +#endif /* TCP_LISTEN_BACKLOG */ + + LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;); + *new_conn = NULL; + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on acceptmbox forever! */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + + if (newconn == NULL) { + /* connection has been aborted */ + NETCONN_SET_SAFE_ERR(conn, ERR_ABRT); + return ERR_ABRT; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + msg.msg.conn = conn; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); +#endif /* TCP_LISTEN_BACKLOG */ + + *new_conn = newconn; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(new_conn); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Receive data: actual implementation that doesn't care whether pbuf or netbuf + * is received + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf/netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +static err_t +netconn_recv_data(struct netconn *conn, void **new_buf) +{ + void *buf = NULL; + u16_t len; + err_t err; +#if LWIP_TCP + struct api_msg msg; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on recvmbox forever! */ + /* @todo: this does not allow us to fetch data that has been put into recvmbox + before the fatal error occurred - is that a problem? */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + if (!netconn_get_noautorecved(conn) || (buf == NULL)) { + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); + } + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (buf == NULL) { + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + /* Avoid to lose any previous error code */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } + len = ((struct pbuf *)buf)->tot_len; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ +#if (LWIP_UDP || LWIP_RAW) + { + LWIP_ASSERT("buf != NULL", buf != NULL); + len = netbuf_len((struct netbuf *)buf); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + +#if LWIP_SO_RCVBUF + SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len)); + + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +} + +/** + * Receive data (in form of a pbuf) from a TCP netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + * ERR_ARG if conn is not a TCP netconn + */ +err_t +netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) +{ + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && + NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;); + + return netconn_recv_data(conn, (void **)new_buf); +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +err_t +netconn_recv(struct netconn *conn, struct netbuf **new_buf) +{ +#if LWIP_TCP + struct netbuf *buf = NULL; + err_t err; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + struct pbuf *p = NULL; + /* This is not a listening netconn, since recvmbox is set */ + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + NETCONN_SET_SAFE_ERR(conn, ERR_MEM); + return ERR_MEM; + } + + err = netconn_recv_data(conn, (void **)&p); + if (err != ERR_OK) { + memp_free(MEMP_NETBUF, buf); + return err; + } + LWIP_ASSERT("p != NULL", p != NULL); + + buf->p = p; + buf->ptr = p; + buf->port = 0; + ipX_addr_set_any(LWIP_IPV6, &buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ + { +#if (LWIP_UDP || LWIP_RAW) + return netconn_recv_data(conn, (void **)new_buf); +#endif /* (LWIP_UDP || LWIP_RAW) */ + } +} + +/** + * TCP: update the receive window: by calling this, the application + * tells the stack that it has processed data and is able to accept + * new data. + * ATTENTION: use with care, this is mainly used for sockets! + * Can only be used when calling netconn_set_noautorecved(conn, 1) before. + * + * @param conn the netconn for which to update the receive window + * @param length amount of data processed (ATTENTION: this must be accurate!) + */ +void +netconn_recved(struct netconn *conn, u32_t length) +{ +#if LWIP_TCP + if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && + (netconn_get_noautorecved(conn))) { + struct api_msg msg; + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.msg.conn = conn; + msg.msg.msg.r.len = length; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); + } +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(length); +#endif /* LWIP_TCP */ +} + +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) +{ + if (buf != NULL) { + ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &buf->addr, addr); + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + msg.msg.conn = conn; + msg.msg.msg.b = buf; + TCPIP_APIMSG(&msg, lwip_netconn_do_send, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY: data will be copied into memory belonging to the stack + * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent + * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once + * @param bytes_written pointer to a location that receives the number of written bytes + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) +{ + struct api_msg msg; + err_t err; + u8_t dontblock; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + if (dontblock && !bytes_written) { + /* This implies netconn_write() cannot be used for non-blocking send, since + it has no way to return the number of bytes written. */ + return ERR_VAL; + } + + /* non-blocking write sends as much */ + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + msg.msg.msg.w.time_started = sys_now(); + } else { + msg.msg.msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + TCPIP_APIMSG(&msg, lwip_netconn_do_write, err); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock +#if LWIP_SO_SNDTIMEO + || (conn->send_timeout != 0) +#endif /* LWIP_SO_SNDTIMEO */ + ) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = msg.msg.msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close ot shutdown a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = lwip_netconn_do_close; + msg.msg.conn = conn; + /* shutting down both ends is the same as closing */ + msg.msg.msg.sd.shut = how; + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close, + don't use TCPIP_APIMSG here */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param netif_addr the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + ip_addr_t *multiaddr, + ip_addr_t *netif_addr, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr); + msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr); + msg.msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated ip_addr_t where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, ip_addr_t *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + err = sys_sem_new(&sem, 0); + if (err != ERR_OK) { + return err; + } + + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = &sem; + + tcpip_callback(lwip_netconn_do_gethostbyname, &msg); + sys_sem_wait(&sem); + sys_sem_free(&sem); + + return err; +} +#endif /* LWIP_DNS*/ + +#endif /* LWIP_NETCONN */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/api_msg.c b/app/src/main/jni/badvpn/lwip/src/api/api_msg.c new file mode 100644 index 0000000..b1a9b77 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/api_msg.c @@ -0,0 +1,1610 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/mld6.h" + +#include + +#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0) +#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0) + +/* forward declarations */ +#if LWIP_TCP +static err_t lwip_netconn_do_writemore(struct netconn *conn); +static void lwip_netconn_do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; + + LWIP_UNUSED_ARG(addr); + conn = (struct netconn *)arg; + + if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { +#if LWIP_SO_RCVBUF + int recv_avail; + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { + return 0; + } +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if (q != NULL) { + u16_t len; + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr()); + buf->port = pcb->protocol; + + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; + u16_t len; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = ipX_next_header_ptr(); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + len = p->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (conn == NULL) { + return ERR_VAL; + } + if (!sys_mbox_valid(&conn->recvmbox)) { + /* recvmbox already deleted */ + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + return ERR_OK; + } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ + + /* don't overwrite fatal errors! */ + NETCONN_SET_SAFE_ERR(conn, err); + + if (p != NULL) { + len = p->tot_len; + } else { + len = 0; + } + + if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ + return ERR_MEM; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_do_close_internal(conn); + } + /* @todo: implement connect timeout here? */ + + /* Did a nonblocking write fail before? Then check available write-space. */ + if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_do_close_internal(conn); + } + + if (conn) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + enum netconn_state old_state; + SYS_ARCH_DECL_PROTECT(lev); + + conn = (struct netconn *)arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + /* no check since this is always fatal! */ + SYS_ARCH_PROTECT(lev); + conn->last_err = err; + SYS_ARCH_UNPROTECT(lev); + + /* reset conn->state now before waking up other threads */ + old_state = conn->state; + conn->state = NETCONN_NONE; + + /* Notify the user layer about a connection error. Used to signal + select. */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + /* Try to release selects pending on 'read' or 'write', too. + They will get an error if they actually try to read or write. */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + /* pass NULL-message to recvmbox to wake up pending recv */ + if (sys_mbox_valid(&conn->recvmbox)) { + /* use trypost to prevent deadlock */ + sys_mbox_trypost(&conn->recvmbox, NULL); + } + /* pass NULL-message to acceptmbox to wake up pending accept */ + if (sys_mbox_valid(&conn->acceptmbox)) { + /* use trypost to preven deadlock */ + sys_mbox_trypost(&conn->acceptmbox, NULL); + } + + if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || + (old_state == NETCONN_CONNECT)) { + /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary + since the pcb has already been deleted! */ + int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + + if (!was_nonblocking_connect) { + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(&conn->op_completed); + } + } else { + LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, 4); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn = (struct netconn *)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); + + if (!sys_mbox_valid(&conn->acceptmbox)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); + return ERR_VAL; + } + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + /* no protection: when creating the pcb, the netconn is not yet known + to the application thread */ + newconn->last_err = err; + + if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), + so do nothing here! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 4); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ + newconn->pcb.tcp = NULL; + /* no need to drain since we know the recvmbox is empty. */ + sys_mbox_free(&newconn->recvmbox); + sys_mbox_set_invalid(&newconn->recvmbox); + netconn_free(newconn); + return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from lwip_netconn_do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static void +pcb_new(struct api_msg_msg *msg) +{ + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw != NULL) { + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp != NULL) { +#if LWIP_UDPLITE + if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp != NULL) { + setup_tcp(msg->conn); + } + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + return; + } + if (msg->conn->pcb.ip == NULL) { + msg->err = ERR_MEM; + } +#if LWIP_IPV6 + else { + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + ip_set_v6(msg->conn->pcb.ip, 1); + } + } +#endif /* LWIP_IPV6 */ +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +lwip_netconn_do_newconn(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = (struct netconn *)memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->last_err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + + /* If all sizes are the same, every compiler should optimize this switch to nothing, */ + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + goto free_and_return; + } + + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + goto free_and_return; + } + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + sys_sem_free(&conn->op_completed); + goto free_and_return; + } + +#if LWIP_TCP + sys_mbox_set_invalid(&conn->acceptmbox); +#endif + conn->state = NETCONN_NONE; +#if LWIP_SOCKET + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; +#endif /* LWIP_SOCKET */ + conn->callback = callback; +#if LWIP_TCP + conn->current_msg = NULL; + conn->write_offset = 0; +#endif /* LWIP_TCP */ +#if LWIP_SO_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; +#endif /* LWIP_SO_RCVBUF */ + conn->flags = 0; + return conn; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + LWIP_ASSERT("recvmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("acceptmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); + + memp_free(MEMP_NETCONN, conn); +} + +/** + * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in + * these mboxes + * + * @param conn the netconn to free + * @bytes_drained bytes drained from recvmbox + * @accepts_drained pending connections drained from acceptmbox + */ +static void +netconn_drain(struct netconn *conn) +{ + void *mem; +#if LWIP_TCP + struct pbuf *p; +#endif /* LWIP_TCP */ + + /* This runs in tcpip_thread, so we don't need to lock against rx packets */ + + /* Delete and drain the recvmbox. */ + if (sys_mbox_valid(&conn->recvmbox)) { + while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { +#if LWIP_TCP + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { + if(mem != NULL) { + p = (struct pbuf*)mem; + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_recved(conn->pcb.tcp, p->tot_len); + } + pbuf_free(p); + } + } else +#endif /* LWIP_TCP */ + { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(&conn->recvmbox); + sys_mbox_set_invalid(&conn->recvmbox); + } + + /* Delete and drain the acceptmbox. */ +#if LWIP_TCP + if (sys_mbox_valid(&conn->acceptmbox)) { + while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + struct netconn *newconn = (struct netconn *)mem; + /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */ + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_accepted(conn->pcb.tcp); + } + /* drain recvmbox */ + netconn_drain(newconn); + if (newconn->pcb.tcp != NULL) { + tcp_abort(newconn->pcb.tcp); + newconn->pcb.tcp = NULL; + } + netconn_free(newconn); + } + sys_mbox_free(&conn->acceptmbox); + sys_mbox_set_invalid(&conn->acceptmbox); + } +#endif /* LWIP_TCP */ +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static void +lwip_netconn_do_close_internal(struct netconn *conn) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing */ + close = shut == NETCONN_SHUT_RDWR; + + /* Set back some callback pointers */ + if (close) { + tcp_arg(conn->pcb.tcp, NULL); + } + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } + if (shut_tx) { + tcp_sent(conn->pcb.tcp, NULL); + } + if (close) { + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + } + /* Try to close the connection */ + if (close) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx); + } + if (err == ERR_OK) { + /* Closing succeeded */ + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (close) { + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + /* wake up the application task */ + sys_sem_signal(&conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + /* don't restore recv callback: we don't want to receive any more data */ + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_delconn(struct api_msg_msg *msg) +{ + /* @todo TCP: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && + (msg->conn->state != NETCONN_LISTEN) && + (msg->conn->state != NETCONN_CONNECT)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else { + LWIP_ASSERT("blocking connect in progress", + (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn)); + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + + if (msg->conn->pcb.tcp != NULL) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->msg.sd.shut = NETCONN_SHUT_RDWR; + msg->conn->current_msg = msg; + lwip_netconn_do_close_internal(msg->conn); + /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + msg->conn->pcb.tcp = NULL; + } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + } + if (sys_sem_valid(&msg->conn->op_completed)) { + sys_sem_signal(&msg->conn->op_completed); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +lwip_netconn_do_bind(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_VAL; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + + LWIP_UNUSED_ARG(pcb); + + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + + LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn)); + + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + } + if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (!was_blocking) { + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(&conn->op_completed); + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +lwip_netconn_do_connect(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp == NULL) { + /* This may happen when calling netconn_connect() a second time */ + msg->err = ERR_CLSD; + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; + } + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, lwip_netconn_do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } + } + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), + so use TCPIP_APIMSG_ACK() here. */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void +lwip_netconn_do_disconnect(struct api_msg_msg *msg) +{ +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + msg->err = ERR_OK; + } else +#endif /* LWIP_UDP */ + { + msg->err = ERR_VAL; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_listen(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { + struct tcp_pcb* lpcb; +#if LWIP_IPV6 + if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen_dual(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } else +#endif /* LWIP_IPV6 */ + { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } + if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ + msg->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (sys_mbox_valid(&msg->conn->recvmbox)) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(&msg->conn->recvmbox); + sys_mbox_set_invalid(&msg->conn->recvmbox); + } + msg->err = ERR_OK; + if (!sys_mbox_valid(&msg->conn->acceptmbox)) { + msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE); + } + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; + } + } + } + } else { + msg->err = ERR_ARG; + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_send(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr)); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } else { + msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, + ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_recv(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + u32_t remaining = msg->msg.r.len; + do { + u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining; + tcp_recved(msg->conn->pcb.tcp, recved); + remaining -= recved; + }while(remaining != 0); + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from lwip_netconn_do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +lwip_netconn_do_writemore(struct netconn *conn) +{ + err_t err; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock = netconn_is_nonblocking(conn) || + (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); + u8_t apiflags = conn->current_msg->msg.w.apiflags; + + LWIP_ASSERT("conn != NULL", conn != NULL); + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); + +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; + if (dontblock){ + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + /* if OK or memory error, check available space */ + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } + + if (err == ERR_OK) { + conn->write_offset += len; + if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if ((err == ERR_MEM) && !dontblock) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + tcp_output(conn->pcb.tcp); + +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } + } + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) +#endif + { + sys_sem_signal(&conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_write(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { +#if LWIP_TCP + if (msg->conn->state != NETCONN_NONE) { + /* netconn is connecting, closing or in blocking write */ + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by lwip_netconn_do_writemore */ + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); + msg->conn->current_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED; + if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(&msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG + since lwip_netconn_do_writemore ACKs it! */ + return; + } else { + msg->err = ERR_CONN; + } +#else /* LWIP_TCP */ + msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + if (msg->msg.ad.local) { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->local_ip); + } else { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->remote_ip); + } + msg->err = ERR_OK; + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("invalid netconn_type", 0); + break; + } + } else { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + /* @todo: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) { + if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { + /* LISTEN doesn't support half shutdown */ + msg->err = ERR_CONN; + } else { + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + lwip_netconn_do_close_internal(msg->conn); + /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_VAL; + } + sys_sem_signal(&msg->conn->op_completed); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_join_leave_group(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP +#if LWIP_IPV6 && LWIP_IPV6_MLD + if (PCB_ISIPV6(msg->conn->pcb.udp)) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } else { + msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } + } + else +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + { +#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } else { + msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } +#endif /* LWIP_IGMP */ + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } else { + msg->err = ERR_CONN; + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +lwip_netconn_do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/err.c b/app/src/main/jni/badvpn/lwip/src/api/err.c new file mode 100644 index 0000000..92fa8b7 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/err.c @@ -0,0 +1,75 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Operation would block.", /* ERR_WOULDBLOCK -7 */ + "Address in use.", /* ERR_USE -8 */ + "Already connected.", /* ERR_ISCONN -9 */ + "Connection aborted.", /* ERR_ABRT -10 */ + "Connection reset.", /* ERR_RST -11 */ + "Connection closed.", /* ERR_CLSD -12 */ + "Not connected.", /* ERR_CONN -13 */ + "Illegal argument.", /* ERR_ARG -14 */ + "Low-level netif error.", /* ERR_IF -15 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + return err_strerr[-err]; + +} + +#endif /* LWIP_DEBUG */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/netbuf.c b/app/src/main/jni/badvpn/lwip/src/api/netbuf.c new file mode 100644 index 0000000..0ccd2bc --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/netbuf.c @@ -0,0 +1,245 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + ipX_addr_set_any(LWIP_IPV6, &buf->addr); + buf->port = 0; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + buf->flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + buf->toport_chksum = 0; +#if LWIP_NETBUF_RECVINFO + ipX_addr_set_any(LWIP_IPV6, &buf->toaddr); +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + * + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + * + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/netdb.c b/app/src/main/jni/badvpn/lwip/src/api/netdb.c new file mode 100644 index 0000000..6a4bac5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/netdb.c @@ -0,0 +1,353 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" +#include "lwip/dns.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + ip_addr_t *addr_list[2]; + ip_addr_t addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + ip_addr_t addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE ip_addr_t s_hostent_addr; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = NULL; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases)); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == NULL)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &h->addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = HOST_NOT_FOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; + h->aliases = NULL; + ret->h_name = hostname; + ret->h_aliases = &h->aliases; + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&h->addr_list; + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + memp_free(MEMP_NETDB, ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + ip_addr_set_loopback(&addr); + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); + if (nodename != NULL) { + namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", + total_size <= NETDB_ELEM_SIZE); + ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + inet_addr_from_ipaddr(&sa->sin_addr, &addr); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons((u16_t)port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + memp_free(MEMP_NETDB, ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/netifapi.c b/app/src/main/jni/badvpn/lwip/src/api/netifapi.c new file mode 100644 index 0000000..81403f8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/netifapi.c @@ -0,0 +1,160 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +static void +netifapi_do_netif_add(struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +static void +netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw); + msg->err = ERR_OK; + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +static void +netifapi_do_netif_common(struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc != NULL) { + msg->err = msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_set_addr; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/sockets.c b/app/src/main/jni/badvpn/lwip/src/api/sockets.c new file mode 100644 index 0000000..6603671 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/sockets.c @@ -0,0 +1,2555 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/pbuf.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \ + (sin)->sin_len = sizeof(struct sockaddr_in); \ + (sin)->sin_family = AF_INET; \ + (sin)->sin_port = htons((port)); \ + inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \ + memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) +#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \ + inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \ + (port) = ntohs((sin)->sin_port); }while(0) + +#if LWIP_IPV6 +#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ + ((namelen) == sizeof(struct sockaddr_in6))) +#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ + ((name)->sa_family == AF_INET6)) +#define SOCK_ADDR_TYPE_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \ + (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ + (sin6)->sin6_family = AF_INET6; \ + (sin6)->sin6_port = htons((port)); \ + (sin6)->sin6_flowinfo = 0; \ + inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0) +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \ + inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \ + (port) = ntohs((sin6)->sin6_port); }while(0) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ + (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) +#else /* LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#endif /* LWIP_IPV6 */ + +#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ + IS_SOCK_ADDR_TYPE_VALID(name)) +#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ + SOCK_ADDR_TYPE_MATCH(name, sock)) +#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) + + + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_sock { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + void *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was ACKed (free send buffer), set by event_callback(), + tested by select */ + u16_t sendevent; + /** error happened for this socket, set by event_callback(), tested by select */ + u16_t errevent; + /** last error that occurred on this socket */ + int err; + /** counter of how many threads are waiting for this socket using select */ + int select_waiting; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** Pointer to the previous waiting task */ + struct lwip_select_cb *prev; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; +}; + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + /** socket index for which to change options */ + int s; +#endif /* LWIP_DEBUG */ + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; + +/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ + * sockaddr_in6 if instantiated. + */ +union sockaddr_aligned { + struct sockaddr sa; +#if LWIP_IPV6 + struct sockaddr_in6 sin6; +#endif /* LWIP_IPV6 */ + struct sockaddr_in sin; +}; + + +/** The global array of available sockets */ +static struct lwip_sock sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; +/** This counter is increased from lwip_select when the list is chagned + and checked in event_callback to see if it has changed. */ +static volatile int select_cb_ctr; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ISCONN -9 Already connected. */ + ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */ + ECONNRESET, /* ERR_RST -11 Connection reset. */ + ENOTCONN, /* ERR_CLSD -12 Connection closed. */ + ENOTCONN, /* ERR_CONN -13 Not connected. */ + EIO, /* ERR_ARG -14 Illegal argument. */ + -1, /* ERR_IF -15 Low-level netif error */ +}; + +#define ERR_TO_ERRNO_TABLE_SIZE \ + (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) + +#define err_to_errno(err) \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ + err_to_errno_table[-(err)] : EIO) + +#ifdef ERRNO +#ifndef set_errno +#define set_errno(err) errno = (err) +#endif +#else /* ERRNO */ +#define set_errno(err) +#endif /* ERRNO */ + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); + +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +get_socket(int s) +{ + struct lwip_sock *sock; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Same as get_socket but doesn't set errno + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +tryget_socket(int s) +{ + if ((s < 0) || (s >= NUM_SOCKETS)) { + return NULL; + } + if (!sockets[s].conn) { + return NULL; + } + return &sockets[s]; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @param accepted 1 if socket has been created by accept(), + * 0 if socket has been created by socket() + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn, int accepted) +{ + int i; + SYS_ARCH_DECL_PROTECT(lev); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + if (!sockets[i].conn) { + sockets[i].conn = newconn; + /* The socket is not yet known to anyone, so no need to protect + after having marked it as used. */ + SYS_ARCH_UNPROTECT(lev); + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + /* TCP sendbuf is empty, but the socket is not yet writable until connected + * (unless it has been created by accept()). */ + sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].errevent = 0; + sockets[i].err = 0; + sockets[i].select_waiting = 0; + return i; + } + SYS_ARCH_UNPROTECT(lev); + } + return -1; +} + +/** Free a socket. The socket's netconn must have been + * delete before! + * + * @param sock the socket to free + * @param is_tcp != 0 for TCP sockets, used to free lastdata + */ +static void +free_socket(struct lwip_sock *sock, int is_tcp) +{ + void *lastdata; + SYS_ARCH_DECL_PROTECT(lev); + + lastdata = sock->lastdata; + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->err = 0; + + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + sock->conn = NULL; + SYS_ARCH_UNPROTECT(lev); + /* don't use 'sock' after this line, as another task might have allocated it */ + + if (lastdata != NULL) { + if (is_tcp) { + pbuf_free((struct pbuf *)lastdata); + } else { + netbuf_delete((struct netbuf *)lastdata); + } + } +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_sock *sock, *nsock; + struct netconn *newconn; + ipX_addr_t naddr; + u16_t port = 0; + int newsock; + err_t err; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* wait for a new connection */ + err = netconn_accept(sock->conn, &newconn); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("newconn != NULL", newconn != NULL); + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(newconn, 1); + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (addr != NULL) { + union sockaddr_aligned tempaddr; + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, ipX_2_ip(&naddr), &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port); + if (*addrlen > tempaddr.sa.sa_len) { + *addrlen = tempaddr.sa.sa_len; + } + MEMCPY(addr, &tempaddr, *addrlen); + } + + newsock = alloc_socket(newconn, 1); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback); + nsock = &sockets[newsock]; + + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + SYS_ARCH_PROTECT(lev); + nsock->rcvevent += (s16_t)(-1 - newconn->socket); + newconn->socket = newsock; + SYS_ARCH_UNPROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock)); + if (addr != NULL) { + LWIP_DEBUGF(SOCKETS_DEBUG, (" addr=")); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + } + + sock_set_errno(sock, 0); + return newsock; +} + +int +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + ipX_addr_t local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); + + err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_sock *sock; + int is_tcp = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if(sock->conn != NULL) { + is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; + } else { + LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); + } + + netconn_delete(sock->conn); + + free_socket(sock, is_tcp); + set_errno(0); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + if (name->sa_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ipX_addr_t remote_addr; + u16_t remote_port; + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); + + err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_sock *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* limit the "backlog" parameter to fit in an u8_t */ + backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff); + + err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_sock *sock; + void *buf = NULL; + struct pbuf *p; + u16_t buflen, copylen; + int off = 0; + u8_t done = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); + } else { + err = netconn_recv(sock->conn, (struct netbuf **)&buf); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n", + err, buf)); + + if (err != ERR_OK) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err))); + sock_set_errno(sock, err_to_errno(err)); + if (err == ERR_CLSD) { + return 0; + } else { + return -1; + } + } + LWIP_ASSERT("buf != NULL", buf != NULL); + sock->lastdata = buf; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + p = (struct pbuf *)buf; + } else { + p = ((struct netbuf *)buf)->p; + } + buflen = p->tot_len; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ( (len <= 0) || + (p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK)!=0)) { + done = 1; + } + } else { + done = 1; + } + + /* Check to see from where the data was.*/ + if (done) { +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { + u16_t port; + ipX_addr_t tmpaddr; + ipX_addr_t *fromaddr; + union sockaddr_aligned saddr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr = &tmpaddr; + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0); + } else { + port = netbuf_fromport((struct netbuf *)buf); + fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf); + } + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, fromaddr, port); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, fromaddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#if SOCKETS_DEBUG + if (from && fromlen) +#endif /* SOCKETS_DEBUG */ + { + if (*fromlen > saddr.sa.sa_len) { + *fromlen = saddr.sa.sa_len; + } + MEMCPY(from, &saddr, *fromlen); + } + } + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK) == 0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + } + } + } while (!done); + + if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + } + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, size_t len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, size_t size, int flags) +{ + struct lwip_sock *sock; + err_t err; + u8_t write_flags; + size_t written; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else /* (LWIP_UDP || LWIP_RAW) */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)written : -1); +} + +int +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct lwip_sock *sock; + err_t err; + u16_t short_size; + u16_t remote_port; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; +#endif + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* @todo: split into multiple sendto's? */ + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + (IS_SOCK_ADDR_LEN_VALID(tolen) && + IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(tolen); + +#if LWIP_TCPIP_CORE_LOCKING + /* Special speedup for fast UDP/RAW sending: call the raw API directly + instead of using the netconn functions. */ + { + struct pbuf* p; + ipX_addr_t *remote_addr; + ipX_addr_t remote_addr_tmp; + +#if LWIP_NETIF_TX_SINGLE_PBUF + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); + if (p != NULL) { +#if LWIP_CHECKSUM_ON_COPY + u16_t chksum = 0; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { + chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + MEMCPY(p->payload, data, size); +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF); + if (p != NULL) { + p->payload = (void*)data; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + if (to != NULL) { + SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6, + to, &remote_addr_tmp, remote_port); + remote_addr = &remote_addr_tmp; + } else { + remote_addr = &sock->conn->pcb.ip->remote_ip; +#if LWIP_UDP + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { + remote_port = sock->conn->pcb.udp->remote_port; + } else +#endif /* LWIP_UDP */ + { + remote_port = 0; + } + } + + LOCK_TCPIP_CORE(); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { +#if LWIP_RAW + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr)); +#else /* LWIP_RAW */ + err = ERR_ARG; +#endif /* LWIP_RAW */ + } +#if LWIP_UDP && LWIP_RAW + else +#endif /* LWIP_UDP && LWIP_RAW */ + { +#if LWIP_UDP +#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF + err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, + ipX_2_ip(remote_addr), remote_port, 1, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ + err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, + ipX_2_ip(remote_addr), remote_port); +#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ +#else /* LWIP_UDP */ + err = ERR_ARG; +#endif /* LWIP_UDP */ + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + /* initialize a buffer */ + buf.p = buf.ptr = NULL; +#if LWIP_CHECKSUM_ON_COPY + buf.flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + if (to) { + SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port); + } else { + remote_port = 0; + ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); + } + netbuf_fromport(&buf) = remote_port; + + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &buf.addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { +#if LWIP_CHECKSUM_ON_COPY + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { + u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); + netbuf_set_chksum(&buf, chksum); + err = ERR_OK; + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + err = netbuf_take(&buf, data, short_size); + } + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + +#if !LWIP_IPV6 + LWIP_UNUSED_ARG(domain); /* @todo: check this */ +#endif /* LWIP_IPV6 */ + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW), + (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) , + event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + if (conn != NULL) { + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(conn, 1); + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn, 0); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, size_t size) +{ + return lwip_send(s, data, size, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset_in: set of sockets to check for read events + * @param writeset_in: set of sockets to check for write events + * @param exceptset_in: set of sockets to check for error events + * @param readset_out: set of sockets that had read events + * @param writeset_out: set of sockets that had write events + * @param exceptset_out: set os sockets that had error events + * @return number of sockets that had events (read/write/exception) (>= 0) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in, + fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_sock *sock; + SYS_ARCH_DECL_PROTECT(lev); + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + void* lastdata = NULL; + s16_t rcvevent = 0; + u16_t sendevent = 0; + u16_t errevent = 0; + /* First get the socket's status (protected)... */ + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + lastdata = sock->lastdata; + rcvevent = sock->rcvevent; + sendevent = sock->sendevent; + errevent = sock->errevent; + } + SYS_ARCH_UNPROTECT(lev); + /* ... then examine it: */ + /* See if netconn of this socket is ready for read */ + if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + /* See if netconn of this socket is ready for write */ + if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + /* See if netconn of this socket had an error */ + if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) { + FD_SET(i, &lexceptset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i)); + nready++; + } + } + /* copy local sets to the ones provided as arguments */ + *readset_out = lreadset; + *writeset_out = lwriteset; + *exceptset_out = lexceptset; + + LWIP_ASSERT("nready >= 0", nready >= 0); + return nready; +} + +/** + * Processing exceptset is not yet implemented. + */ +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + u32_t waitres = 0; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + err_t err; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (s32_t)timeout->tv_sec : (s32_t)-1, + timeout ? (s32_t)timeout->tv_usec : (s32_t)-1)); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* None ready: add our semaphore to list: + We don't actually need any dynamic memory. Our entry on the + list is only valid while we are in this function, so it's ok + to use local variables. */ + + select_cb.next = NULL; + select_cb.prev = NULL; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + err = sys_sem_new(&select_cb.sem, 0); + if (err != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } + + /* Protect the select_cb_list */ + SYS_ARCH_PROTECT(lev); + + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + if (select_cb_list != NULL) { + select_cb_list->prev = &select_cb; + } + select_cb_list = &select_cb; + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + + /* Now we can safely unprotect */ + SYS_ARCH_UNPROTECT(lev); + + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + SYS_ARCH_UNPROTECT(lev); + } + } + + /* Call lwip_selscan again: there could have been events between + the last scan (whithout us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } + } + + waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout); + } + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting--; + LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0); + SYS_ARCH_UNPROTECT(lev); + } + } + /* Take us off the list */ + SYS_ARCH_PROTECT(lev); + if (select_cb.next != NULL) { + select_cb.next->prev = select_cb.prev; + } + if (select_cb_list == &select_cb) { + LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL); + select_cb_list = select_cb.next; + } else { + LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); + select_cb.prev->next = select_cb.next; + } + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + SYS_ARCH_UNPROTECT(lev); + + sys_sem_free(&select_cb.sem); + if (waitres == SYS_ARCH_TIMEOUT) { + /* Timeout */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* See what's set */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); +return_copy_fdsets: + set_errno(0); + if (readset) { + *readset = lreadset; + } + if (writeset) { + *writeset = lwriteset; + } + if (exceptset) { + *exceptset = lexceptset; + } + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_sock *sock; + struct lwip_select_cb *scb; + int last_select_cb_ctr; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + SYS_ARCH_PROTECT(lev); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + SYS_ARCH_UNPROTECT(lev); + return; + } + s = conn->socket; + SYS_ARCH_UNPROTECT(lev); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + SYS_ARCH_PROTECT(lev); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + case NETCONN_EVT_ERROR: + sock->errevent = 1; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + + if (sock->select_waiting == 0) { + /* noone is waiting for this socket, no need to check select_cb_list */ + SYS_ARCH_UNPROTECT(lev); + return; + } + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { + if (scb->readset && FD_ISSET(s, scb->readset)) { + do_signal = 1; + } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; + } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; + } + } + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidagin the semaphore. */ + sys_sem_signal(&scb->sem); + } + } + /* unlock interrupts with each step */ + last_select_cb_ctr = select_cb_ctr; + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; + } + } + SYS_ARCH_UNPROTECT(lev); +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + } else { + sock_set_errno(sock, ENOTCONN); + return ENOTCONN; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if(how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return EINVAL; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_sock *sock; + union sockaddr_aligned saddr; + ipX_addr_t naddr; + u16_t port; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* get the IP address and port */ + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local); + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, &naddr, port); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); + + if (*namelen > saddr.sa.sa_len) { + *namelen = saddr.sa.sa_len; + } + MEMCPY(name, &saddr, *namelen); + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_sock *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; + + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; + case IP_MULTICAST_LOOP: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return 0; + } + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /*case SO_USELOOPBACK: UNIMPL */ + *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = netconn_type(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (netconn_type(sock->conn)) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + /* only overwrite ERR_OK or tempoary errors */ + if ((sock->err == 0) || (sock->err == EINPROGRESS)) { + sock_set_errno(sock, err_to_errno(sock->conn->last_err)); + } + *(int *)optval = sock->err; + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + *(int *)optval = netconn_get_sendtimeout(sock->conn); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = netconn_get_recvtimeout(sock->conn); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; + case IP_MULTICAST_LOOP: + if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { + *(u8_t*)optval = 1; + } else { + *(u8_t*)optval = 0; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", + s, *(int *)optval)); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct lwip_sock *sock = get_socket(s); + err_t err = ERR_OK; + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_LOOP: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (optlen < sizeof(int)) { + err = EINVAL; + } + + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch (level) */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (*(int*)optval) { + ip_set_option(sock->conn->pcb.ip, optname); + } else { + ip_reset_option(sock->conn->pcb.ip, optname); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + netconn_set_recvtimeout(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + netconn_set_recvbufsize(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); + break; + case IP_MULTICAST_LOOP: + if (*(u8_t*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + ip_addr_t if_addr; + ip_addr_t multi_addr; + inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); + inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup(&if_addr, &multi_addr); + } else { + data->err = igmp_leavegroup(&if_addr, &multi_addr); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + if (*(int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); + } else { + tcp_nagle_enable(sock->conn->pcb.tcp); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*(int*)optval) { + sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; + } else { + sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", + s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF + u16_t buflen = 0; + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } +#if LWIP_FIONREAD_LINUXMODE + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + struct pbuf *p; + if (sock->lastdata) { + p = ((struct netbuf *)sock->lastdata)->p; + } else { + struct netbuf *rxbuf; + err_t err; + if (sock->rcvevent <= 0) { + *((u16_t*)argp) = 0; + } else { + err = netconn_recv(sock->conn, &rxbuf); + if (err != ERR_OK) { + *((u16_t*)argp) = 0; + } else { + sock->lastdata = rxbuf; + *((u16_t*)argp) = rxbuf->p->tot_len; + } + } + } + return 0; + } +#endif /* LWIP_FIONREAD_LINUXMODE */ + +#if LWIP_SO_RCVBUF + /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((u16_t*)argp) = (u16_t)recv_avail; + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + struct pbuf *p = (struct pbuf *)sock->lastdata; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; +#else /* LWIP_SO_RCVBUF */ + break; +#endif /* LWIP_SO_RCVBUF */ +#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ + + case FIONBIO: + val = 0; + if (argp && *(u32_t*)argp) { + val = 1; + } + netconn_set_nonblocking(sock->conn, val); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val)); + sock_set_errno(sock, 0); + return 0; + + default: + break; + } /* switch (cmd) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; +} + +/** A minimal implementation of fcntl. + * Currently only the commands F_GETFL and F_SETFL are implemented. + * Only the flag O_NONBLOCK is implemented. + */ +int +lwip_fcntl(int s, int cmd, int val) +{ + struct lwip_sock *sock = get_socket(s); + int ret = -1; + + if (!sock || !sock->conn) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + break; + case F_SETFL: + if ((val & ~O_NONBLOCK) == 0) { + /* only O_NONBLOCK, all other bits are zero */ + netconn_set_nonblocking(sock->conn, val & O_NONBLOCK); + ret = 0; + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + break; + } + return ret; +} + +#endif /* LWIP_SOCKET */ diff --git a/app/src/main/jni/badvpn/lwip/src/api/tcpip.c b/app/src/main/jni/badvpn/lwip/src/api/tcpip.c new file mode 100644 index 0000000..7c1c9ca --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/api/tcpip.c @@ -0,0 +1,492 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static tcpip_init_done_fn tcpip_init_done; +static void *tcpip_init_done_arg; +static sys_mbox_t mbox; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_mutex_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + + if (tcpip_init_done != NULL) { + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + UNLOCK_TCPIP_CORE(); + LWIP_TCPIP_THREAD_ALIVE(); + /* wait for a message, timeouts are processed while waiting */ + sys_timeouts_mbox_fetch(&mbox, (void **)&msg); + LOCK_TCPIP_CORE(); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); + break; +#endif /* LWIP_NETCONN */ + +#if !LWIP_TCPIP_CORE_LOCKING_INPUT + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ETHERNET + if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ETHERNET */ +#if LWIP_IPV6 + if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) { + ip6_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_IPV6 */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + +#if LWIP_TCPIP_TIMEOUT + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif /* LWIP_TCPIP_TIMEOUT */ + + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; + } + } +} + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or + * NETIF_FLAG_ETHERNET flags) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_TCPIP_CORE_LOCKING_INPUT + err_t ret; + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp)); + LOCK_TCPIP_CORE(); +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ret = ethernet_input(p, inp); + } else +#endif /* LWIP_ETHERNET */ + { + ret = ip_input(p, inp); + } + UNLOCK_TCPIP_CORE(); + return ret; +#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + struct tcpip_msg *msg; + + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(&mbox, msg); + } else { + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_TIMEOUT +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} +#endif /* LWIP_TCPIP_TIMEOUT */ + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + if (sys_mbox_valid(&mbox)) { + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(&mbox, &msg); + sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0); + return apimsg->msg.err; + } + return ERR_VAL; +} + +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) +{ + struct tcpip_msg msg; + + if (sys_mbox_valid(&mbox)) { + err_t err = sys_sem_new(&netifapimsg->msg.sem, 0); + if (err != ERR_OK) { + netifapimsg->msg.err = err; + return err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(&mbox, &msg); + sys_sem_wait(&netifapimsg->msg.sem); + sys_sem_free(&netifapimsg->msg.sem); + return netifapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + return sys_mbox_trypost(&mbox, msg); +} + +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(tcpip_init_done_fn initfunc, void *arg) +{ + lwip_init(); + + tcpip_init_done = initfunc; + tcpip_init_done_arg = arg; + if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) { + LWIP_ASSERT("failed to create tcpip_thread mbox", 0); + } +#if LWIP_TCPIP_CORE_LOCKING + if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) { + LWIP_ASSERT("failed to create lock_tcpip_core", 0); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = (struct pbuf *)p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/def.c b/app/src/main/jni/badvpn/lwip/src/core/def.c new file mode 100644 index 0000000..352b552 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/def.c @@ -0,0 +1,108 @@ +/** + * @file + * Common functions used throughout the stack. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +lwip_ntohs(u16_t n) +{ + return lwip_htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +lwip_ntohl(u32_t n) +{ + return lwip_htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/dhcp.c b/app/src/main/jni/badvpn/lwip/src/core/dhcp.c new file mode 100644 index 0000000..21fd784 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/dhcp.c @@ -0,0 +1,1771 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * #define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif + +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 + +#define REBOOT_TRIES 2 + +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +#define DHCP_OPTION_IDX_OVERLOAD 0 +#define DHCP_OPTION_IDX_MSG_TYPE 1 +#define DHCP_OPTION_IDX_SERVER_ID 2 +#define DHCP_OPTION_IDX_LEASE_TIME 3 +#define DHCP_OPTION_IDX_T1 4 +#define DHCP_OPTION_IDX_T2 5 +#define DHCP_OPTION_IDX_SUBNET_MASK 6 +#define DHCP_OPTION_IDX_ROUTER 7 +#define DHCP_OPTION_IDX_DNS_SERVER 8 +#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) + +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + + +/* DHCP client state machine functions */ +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP message, fill in common headers */ +static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type); +/* free a DHCP request */ +static void dhcp_delete_msg(struct dhcp *dhcp); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +#if DHCP_DOES_ARP_CHECK +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_CHECKING); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); +} +#endif /* DHCP_DOES_ARP_CHECK */ + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID))); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->server_ip_addr))); + /* remember offered address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif)); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_REQUESTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* send broadcast to any DHCP server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + */ +void +dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + */ +void +dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this client's request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } +#if DHCP_DOES_ARP_CHECK + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } +#endif /* DHCP_DOES_ARP_CHECK */ + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } else if (dhcp->state == DHCP_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t1_timeout(): must renew\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_RENEWING, not DHCP_BOUND */ + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t2_timeout(): must rebind\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_REBINDING, not DHCP_BOUND */ + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; +#if LWIP_DNS + u8_t n; +#endif /* LWIP_DNS */ + + /* clear options we might not get from the ACK */ + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* lease time given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); + } + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ + ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* subnet mask given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK))); + dhcp->subnet_mask_given = 1; + } else { + dhcp->subnet_mask_given = 0; + } + + /* gateway router */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER))); + } + +#if LWIP_DNS + /* DNS servers */ + for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { + ip_addr_t dns_addr; + ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); + dns_setserver(n, &dns_addr); + } +#endif /* LWIP_DNS */ +} + +/** Set a statically allocated struct dhcp to work with. + * Using this prevents dhcp_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct dhcp + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp != NULL", dhcp != NULL); + LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL); + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + netif->dhcp = dhcp; +} + +/** Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif->dhcp != NULL) { + mem_free(netif->dhcp); + netif->dhcp = NULL; + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* check hwtype of the netif */ + if ((netif->flags & NETIF_FLAG_ETHARP) == 0) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n")); + return ERR_ARG; + } + + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + return ERR_MEM; + } + ip_set_option(dhcp->pcb, SOF_BROADCAST); + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void +dhcp_inform(struct netif *netif) +{ + struct dhcp dhcp; + err_t result = ERR_OK; + struct udp_pcb *pcb; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + memset(&dhcp, 0, sizeof(struct dhcp)); + dhcp_set_state(&dhcp, DHCP_INFORM); + + if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) { + /* re-use existing pcb */ + pcb = netif->dhcp->pcb; + } else { + pcb = udp_new(); + if (pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + return; + } + dhcp.pcb = pcb; + ip_set_option(dhcp.pcb, SOF_BROADCAST); + udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + } + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM); + if (result == ERR_OK) { + dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option_trailer(&dhcp); + + pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(&dhcp); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp.pcb != NULL) { + /* otherwise, the existing pcb was used */ + udp_remove(dhcp.pcb); + } +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_REBINDING: + case DHCP_RENEWING: + case DHCP_BOUND: + case DHCP_REBOOTING: + netif_set_down(netif); + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_OFF: + /* stay off */ + break; + default: + dhcp->tries = 0; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + dhcp_discover(netif); + break; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr) +{ + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(addr))); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif /* DHCP_DOES_ARP_CHECK */ + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); + ip_addr_set_any(&dhcp->offered_ip_addr); + dhcp_set_state(dhcp, DHCP_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + ip_addr_t sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + + if (dhcp->subnet_mask_given) { + /* copy offered network mask */ + ip_addr_copy(sn_mask, dhcp->offered_sn_mask); + } else { + /* subnet mask not given, choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); + if (first_octet <= 127) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); + } + } + + ip_addr_copy(gw_addr, dhcp->offered_gw_addr); + /* gateway address not given? */ + if (ip_addr_isany(&gw_addr)) { + /* copy network address */ + ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); + /* use first host address on network as gateway */ + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL)); + } + +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", + ip4_addr_get_u32(&sn_mask))); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", + ip4_addr_get_u32(&gw_addr))); + netif_set_gw(netif, &gw_addr); + /* bring the interface up */ + netif_set_up(netif); + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + ip_addr_set_zero(&dhcp->server_ip_addr); + ip_addr_set_zero(&dhcp->offered_ip_addr); + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE); + if (result == ERR_OK) { + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + dhcp_set_state(dhcp, DHCP_OFF); + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + */ +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + dhcp->request_timeout = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + u8_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + int parse_file_as_options = 0; + int parse_sname_as_options = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = (struct dhcp_msg *)p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if (offset + 1 < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + /* case(DHCP_OPTION_END): handled above */ + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + break; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + decode_len = 0; + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + if (!dhcp_option_given(dhcp, decode_idx)) { + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + value = ((u8_t*)&value)[0]; + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + if ((offset < offset_max) && offset_max) { + q = q->next; + LWIP_ASSERT("next pbuf was null", q); + options = (u8_t*)q->payload; + } else { + /* We've run out of bytes, probably no end marker. Don't proceed. */ + break; + } + } + } + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME) { + parse_sname_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + parse_sname_as_options = 1; + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); + } +#if LWIP_DHCP_BOOTP_FILE + if (!parse_file_as_options) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS); + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; + } +#endif /* LWIP_DHCP_BOOTP_FILE */ + } + if (parse_file_as_options) { + /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */ + parse_file_as_options = 0; + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (parse_sname_as_options) { + parse_sname_as_options = 0; + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } + return ERR_OK; +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); + goto free_pbuf_and_return; + } + + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } +free_pbuf_and_return: + dhcp->msg_in = NULL; + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + * @param dhcp dhcp control struct + * @param message_type message type of the request + */ +static err_t +dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) +{ + u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ +#else + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_msg(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + xid = LWIP_RAND(); +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + } + dhcp->xid = xid; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + /* we don't need the broadcast flag since we can receive unicast traffic + before being fully configured! */ + dhcp->msg_out->flags = 0; + ip_addr_set_zero(&dhcp->msg_out->ciaddr); + /* set ciaddr to netif->ip_addr based on message_type and state */ + if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || + ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */ + ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) { + ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr); + } + ip_addr_set_zero(&dhcp->msg_out->yiaddr); + ip_addr_set_zero(&dhcp->msg_out->siaddr); + ip_addr_set_zero(&dhcp->msg_out->giaddr); + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + /* Add option MESSAGE_TYPE */ + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, message_type); + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param dhcp the dhcp struct to free the request from + */ +static void +dhcp_delete_msg(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/dns.c b/app/src/main/jni/badvpn/lwip/src/core/dns.c new file mode 100644 index 0000000..90821a6 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/dns.c @@ -0,0 +1,988 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + +/** DNS query message structure. + No packing needed: only used locally on the stack. */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; +}; +#define SIZEOF_DNS_QUERY 4 + +/** DNS answer message structure. + No packing needed: only used locally on the stack. */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; + u32_t ttl; + u16_t len; +}; +#define SIZEOF_DNS_ANSWER 10 + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + ip_addr_t ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#if DNS_LOCAL_HOSTLIST + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; +/** Contiguous buffer for processing responses */ +static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; +static u8_t* dns_payload; + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + ip_addr_t dnsserver; + + dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); + + /* initialize default DNS server address */ + DNS_SERVER_ADDRESS(&dnsserver); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void +dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +ip_addr_t +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +#if DNS_LOCAL_HOSTLIST +static void +dns_init_local() +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + int i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + size_t namelen; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); + namelen = strlen(init_entry->name); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, init_entry->name, namelen); + ((char*)entry->name)[namelen] = 0; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @return The first IP address for the hostname in the local host-list or + * IPADDR_NONE if not found. + */ +static u32_t +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if(strcmp(entry->name, hostname) == 0) { + return ip4_addr_get_u32(&entry->addr); + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + int i; + for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { + if(strcmp(local_hostlist_static[i].name, hostname) == 0) { + return ip4_addr_get_u32(&local_hostlist_static[i].addr); + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return IPADDR_NONE; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Remove all entries from the local host-list for a specific hostname + * and/or IP addess + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int +dns_local_removehost(const char *hostname, const ip_addr_t *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !strcmp(entry->name, hostname)) && + ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + memp_free(MEMP_LOCALHOSTLIST, free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t +dns_local_addhost(const char *hostname, const ip_addr_t *addr) +{ + struct local_hostlist_entry *entry; + size_t namelen; + LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); + namelen = strlen(hostname); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, hostname, namelen); + ((char*)entry->name)[namelen] = 0; + ip_addr_copy(entry->addr, *addr); + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of ip_addr_t to + * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +static u32_t +dns_lookup(const char *name) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) + u32_t addr; +#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ +#if DNS_LOCAL_HOSTLIST + if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOOKUP_LOCAL_EXTERN */ + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return ip4_addr_get_u32(&dns_table[i].ipaddr); + } + } + + return IPADDR_NONE; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 1 + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + u16_t realloc_size; + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = PP_HTONS(1); + query = (char*)hdr + SIZEOF_DNS_HDR; + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = PP_HTONS(DNS_RRTYPE_A); + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + realloc_size = (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))); + LWIP_ASSERT("p->tot_len >= realloc_size", p->tot_len >= realloc_size); + pbuf_realloc(p, realloc_size); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1])) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u16_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u16_t nquestions, nanswers; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr; + } + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; + + while (nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && + (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + if (pEntry->ttl == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + goto flushentry; + } + /* deallocate memory and return */ + goto memerr; + } else { + pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } +flushentry: + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param hostnamelen length of the hostname + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t +dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, + void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + size_t namelen; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); + MEMCPY(pEntry->name, name, namelen); + pEntry->name[namelen] = 0; + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + u32_t ipaddr; + size_t hostnamelen; + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0])) { + return ERR_ARG; + } + hostnamelen = strlen(hostname); + if (hostnamelen >= DNS_MAX_NAME_LENGTH) { + return ERR_ARG; + } + + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname, "localhost")==0) { + ip_addr_set_loopback(addr); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK */ + ipaddr = ipaddr_addr(hostname); + if (ipaddr == IPADDR_NONE) { + /* already have this address cached? */ + ipaddr = dns_lookup(hostname); + } + if (ipaddr != IPADDR_NONE) { + ip4_addr_set_u32(addr, ipaddr); + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, hostnamelen, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/inet_chksum.c b/app/src/main/jni/badvpn/lwip/src/core/inet_chksum.c new file mode 100644 index 0000000..8bc42c1 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/inet_chksum.c @@ -0,0 +1,545 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" + +#include +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +u16_t lwip_standard_chksum(void *dataptr, int len); +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)(void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + u16_t chklen; + + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo_partial: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/init.c b/app/src/main/jni/badvpn/lwip/src/core/init.c new file mode 100644 index 0000000..c24c027 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/init.c @@ -0,0 +1,345 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timers.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#endif /* !MEMP_MEM_MALLOC */ +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND) + #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF + #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" +#endif +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_ACCEPTCONN != SOF_ACCEPTCONN + #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" +#endif +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#if SO_LINGER != SOF_LINGER + #error "WARNING: SO_LINGER != SOF_LINGER" +#endif +#endif /* LWIP_SOCKET */ + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ +#endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + ip6_init(); + nd6_init(); +#if LWIP_IPV6_MLD + mld6_init(); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c new file mode 100644 index 0000000..b122da2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c @@ -0,0 +1,528 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + + +/** Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL); + + /* clear data structure */ + memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif->autoip = autoip; +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + netif->autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if (defend) { + if (netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + ip_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if (netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if (autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if (autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if (autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if (netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if (netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c new file mode 100644 index 0000000..af47153 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c @@ -0,0 +1,338 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the icmp header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + iphdr = (struct ip_hdr *)ip_current_header(); + hlen = IPH_HL(iphdr) * 4; + if (p->len < sizeof(u16_t)*2) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + break; + case ICMP_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(ip_current_dest_addr())) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + ip_addr_copy(iphdr->src, *ip_current_dest_addr()); + ip_addr_copy(iphdr->dest, *ip_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); +#if CHECKSUM_GEN_ICMP + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + /* send an ICMP packet, src addr is the dest addr of the curren packet */ + ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip_addr_t iphdr_src; + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + icmphdr->chksum = 0; + icmphdr->chksum = inet_chksum(icmphdr, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_addr_copy(iphdr_src, iphdr->src); + ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c new file mode 100644 index 0000000..bd52744 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c @@ -0,0 +1,805 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404U +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); +static err_t igmp_remove_group(struct igmp_group *group); +static void igmp_timeout( struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); +static void igmp_send(struct igmp_group *group, u8_t type); + + +static struct igmp_group* igmp_group_list; +static ip_addr_t allsystems; +static ip_addr_t allrouters; + + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->netif == netif) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->netif = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the igmp header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) +{ + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + if (p->len < IGMP_MINLEN) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip_addr_cmp(dest, &allsystems)) { + ip_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->netif)); + IGMP_STATS_INC(igmp.proterr); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /* ensure the input value is > 0 */ + if (max_time == 0) { + max_time = 1; + } +#ifdef LWIP_RAND + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % (max_time - 1)) + 1; +#endif /* LWIP_RAND */ +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip_addr_t src = *IP_ADDR_ANY; + ip_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_copy(src, group->netif->ip_addr); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, group->netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IGMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c new file mode 100644 index 0000000..1acc255 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c @@ -0,0 +1,924 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#define LWIP_INLINE_IP_CHKSUM 1 +#endif +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(ip_addr_t *dest) +{ + struct netif *netif; + +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + /* iterate through netifs */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr())); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + if (!ip_canforward(p)) { + goto return_noroute; + } + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(ip_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(ip_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ip_current_dest_addr()); + return; +return_noroute: + snmp_inc_ipoutnoroutes(); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(ip_current_dest_addr())) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(ip_current_dest_addr(), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if ((netif->autoip != NULL) && + ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && !ip_addr_isany(ip_current_src_addr())) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || + (ip_addr_ismulticast(ip_current_src_addr()))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IPH_PROTO(iphdr) == IP_PROTO_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + ip_data.current_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ip_current_dest_addr()); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && + !ip_addr_ismulticast(ip_current_dest_addr())) { + pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip_addr_set_any(ip_current_src_addr()); + ip_addr_set_any(ip_current_dest_addr()); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(proto, ttl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_copy(iphdr->src, netif->ip_addr); + } else { + /* src cannot be NULL here */ + ip_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + iphdr->_chksum = chk_sum; /* network order */ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p, dest); + } +#endif /* LWIP_IGMP */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c new file mode 100644 index 0000000..8f633ff --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c @@ -0,0 +1,312 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any = { IPADDR_ANY }; +const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast(u32_t addr, const struct netif *netif) +{ + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) { + return 0; + /* on the same (sub) network... */ + } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(&netif->netmask)) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip_addr_t val; + + if (ipaddr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return (0); + } + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) { + return (0); + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, htonl(val)); + } + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ipaddr_ntoa(const ip_addr_t *addr) +{ + static char str[16]; + return ipaddr_ntoa_r(addr, str, 16); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c new file mode 100644 index 0000000..8d18434 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c @@ -0,0 +1,863 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if(pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/README b/app/src/main/jni/badvpn/lwip/src/core/ipv6/README new file mode 100644 index 0000000..3620004 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c new file mode 100644 index 0000000..9656c3b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c @@ -0,0 +1,50 @@ +/** + * @file + * + * DHCPv6. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + + +#endif /* LWIP_IPV6_DHCP6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c new file mode 100644 index 0000000..ab9783a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c @@ -0,0 +1,193 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" + +#include + +#define ETHTYPE_IPV6 0x86DD + +/** The ethernet address */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Ethernet header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +/** + * Send an IPv6 packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!", + (netif->hwaddr_len == 6)); + SMEMCPY(ðhdr->dest, dst, 6); + SMEMCPY(ðhdr->src, src, 6); + ethhdr->type = PP_HTONS(ETHTYPE_IPV6); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ... + * + * @TODO anycast addresses + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + s8_t i; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + return ERR_BUF; + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We have a unicast destination IP address */ + /* TODO anycast? */ + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return ERR_MEM; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */ + if ((neighbor_cache[i].state == ND6_REACHABLE) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Send out. */ + SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We should queue packet on this interface. */ + pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); + return nd6_queue_packet(i, q); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c new file mode 100644 index 0000000..ea82682 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c @@ -0,0 +1,337 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if LWIP_ICMP6_CHECKSUM_CHECK + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_ICMP6_CHECKSUM_CHECK */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; + break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* TODO implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; + break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t *reply_src, *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(netif, reply_dest); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c new file mode 100644 index 0000000..bdf4ff4 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c @@ -0,0 +1,51 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet6.h" + +/** @see ip6_addr.c for implementation of functions. */ + +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c new file mode 100644 index 0000000..1eb91f9 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c @@ -0,0 +1,1034 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(struct ip6_addr *src, struct ip6_addr *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif. */ + return netif_default; + } + + /* Try to find the netif for the source address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif */ + return netif_default; + } + + /* See if the destination subnet matches a configured address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* Get the netif for a suitable router. */ + i = nd6_select_router(dest, NULL); + if (i >= 0) { + if (default_router_list[i].neighbor_entry != NULL) { + if (default_router_list[i].neighbor_entry->netif != NULL) { + return default_router_list[i].neighbor_entry->netif; + } + } + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +ip6_addr_t * +ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) +{ + ip6_addr_t * src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip6_addr(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* TODO find longest matching prefix. */ + if ((!(ip6_addr_netcmp(src, dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip6_addr(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + /* do not forward link-local addresses */ + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + + +/** + * This function is called by the network interface device driver when + * an IPv6 packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); + ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } + else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + /* Do not match link-local addresses to other netifs. */ + netif = NULL; + break; + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IP6H_NEXTH(ip6hdr) == IP6_NEXTH_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr * frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && + ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { + + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -hlen); + } + else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + pbuf_header(p, ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); + ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points + * to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY; + } + else if (ip6_addr_isany(src)) { + src = ip6_select_source_address(netif, dest); + if ((src == NULL) || ip6_addr_isany(src)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + /* TODO implement loopback for v6 + if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { + return netif_loop_output(netif, p, dest); + }*/ +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr * hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + IP6H_PLEN(ip6hdr), + IP6H_NEXTH(ip6hdr), + IP6H_HOPLIM(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..65d2798 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c @@ -0,0 +1,251 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY in ip6_addr.h */ +const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; + +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii represenation (e.g. "FF01::1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char * s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') + zero_blocks--; + else if (!isxdigit(*s)) + break; + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } if (s[1] == ':') { + s++; + /* "::" found, set zeros */ + while (zero_blocks-- > 0) { + if (current_block_index & 0x1) { + addr_index++; + } + else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + } + } + } else if (isxdigit(*s)) { + /* add current digit */ + current_block_value = (current_block_value << 4) + + (isdigit(*s) ? *s - '0' : + 10 + (islower(*s) ? *s - 'a' : *s - 'A')); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; + + i = 0; + zero_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + if (current_block_value == 0) { + /* generate empty block "::" */ + if (!zero_flag) { + if (current_block_index > 0) { + zero_flag = 1; + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + } + } + else { + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } + else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) return NULL; + + zero_flag = 0; + } + } + + buf[i] = 0; + + return buf; +} +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c new file mode 100644 index 0000000..a43fd61 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c @@ -0,0 +1,697 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* static variables */ +static struct ip6_reassdata *reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, move back to the original header (we are now pointing to Fragment header). */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip6_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + + /* Then, unchain the struct ip6_reassdata from the list and free it. */ + if (ipr == reassdatagrams) { + reassdatagrams = ipr->next; + } else { + prev = reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @param len the length of the payload (after Fragment Header) + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr * frag_hdr; + u16_t offset, len; + u8_t clen, valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = ntohs(ip6_current_header()->_plen); + len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; + len -= IP6_FRAG_HLEN; + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ + iprh = (struct ip6_reass_helper *)p->payload; + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = p; + } + } + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while(iprh != NULL) { + + if (iprh->next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; + + /* hide the fragment header for every succeding fragment */ + pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); + pbuf_cat(ipr->p, iprh->next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + + IP6_FRAG_HLEN + - IP6_HLEN ; + + /* Set payload length in ip header. */ + ipr->iphdr->_plen = htons(ipr->datagram_len); + + /* Get the furst pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ip6_frag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr * frag_hdr; + struct pbuf *rambuf; + struct pbuf *newpbuf; + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + u16_t newpbuflen = 0; + u16_t left_to_copy; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c new file mode 100644 index 0000000..1cb2dd9 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c @@ -0,0 +1,580 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + + +/* The list of joined groups. */ +static struct mld_group* mld_group_list; + + +/* Forward declarations. */ +static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); +static err_t mld6_free_group(struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + struct mld_group *prev = NULL; + struct mld_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == mld_group_list) { + mld_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->netif == netif) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + group->netif = ifp; + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = mld_group_list; + + mld_group_list = group; + } + + return group; +} + +/** + * Remove a group in the mld_group_list and free + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_free_group(struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (mld_group_list == group) { + mld_group_list = group->next; + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header * mld_hdr; + struct mld_group* group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* TODO debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + { + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = mld_group_list; + while (group != NULL) { + if ((group->netif == inp) && + (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } + else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + } + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + } + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + { + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + } + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + err = ERR_OK; + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave if there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); + } + + /* Free the group */ + mld6_free_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = (LWIP_RAND() % (maxresp - 1)) + 1; +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct mld_group *group, u8_t type) +{ + struct mld_header * mld_hdr; + struct pbuf * p; + ip6_addr_t * src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { + /* We couldn't allocate a suitable pbuf. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(group->netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); + pbuf_free(p); +} + + + +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c b/app/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c new file mode 100644 index 0000000..480638e --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c @@ -0,0 +1,1787 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); +static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); +static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static void nd6_send_rs(struct netif * netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + /* We are using a duplicate address. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + +#if LWIP_IPV6_MLD + /* Leave solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]); + mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address); +#endif /* LWIP_IPV6_MLD */ + + + + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } + else { + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + neighbor_cache[i].netif = inp; + neighbor_cache[i].counter.reachable_time = reachable_time; + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + neighbor_cache[i].state = ND6_REACHABLE; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + lladdr_opt = NULL; + if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } + else { + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if ( i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + else + { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header * ra_hdr; + u8_t * buffer; /* Used to copy options. */ + u16_t offset; + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + inp->rs_count = 0; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = ra_hdr->retrans_timer; + } + if (ra_hdr->reachable_time > 0) { + reachable_time = ra_hdr->reachable_time; + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* TODO set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option * lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option * mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (mtu_opt->mtu >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = mtu_opt->mtu; +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option * prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { + /* Add to on-link prefix list. */ + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i < 0) { + /* Create a new cache entry. */ + i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); + } + if (i >= 0) { + prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime; + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + { + /* TODO implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; + } + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header * redir_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + lladdr_opt = NULL; + if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + destination_cache[i].pmtu = icmp6hdr->data; + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i, j; + struct netif * netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } + else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } + else { + neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_PROBE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remainig. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + prefix_list[i].invalidation_timer = 0; + } + if ((prefix_list[i].invalidation_timer > 0) && + (prefix_list[i].netif != NULL)) { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; + prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; + prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; + prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_istentative(netif->ip6_addr_state[i])) { + if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; + /* TODO implement preferred and valid lifetimes. */ + } + else if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_IPV6_MLD + if ((netif->ip6_addr_state[i] & 0x07) == 0) { + /* Join solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); + mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); + } +#endif /* LWIP_IPV6_MLD */ + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + (netif->ip6_addr_state[i])++; + /* TODO send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { + nd6_send_rs(netif); + netif->rs_count--; + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + ip6_addr_t * dest_addr; + + /* Use link-local address as source address. */ + /* src_addr = &(netif->ip6_addr[0]); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } + else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } + else { + dest_addr = ip6_current_src_addr(); + } + + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static void +nd6_send_rs(struct netif * netif) +{ + struct rs_header * rs_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + u16_t packet_len; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } + else { + src_addr = IP6_ADDR_ANY; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + packet_len = sizeof(struct rs_header); + if (src_addr != IP6_ADDR_ANY) { + packet_len += sizeof(struct lladdr_option); + } + p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); + if ((p == NULL) || (p->len < packet_len)) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +s8_t +nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* TODO: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 0; + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { + if (default_router_list[router_index].neighbor_entry == NULL) { + default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); + return router_index; + } + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); +#if LWIP_IPV6_AUTOCONFIG + prefix_list[i].flags = 0; +#endif + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +s8_t +nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } + else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, netif)) { + /* Destination in local link. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); + } + else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } + else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 0; + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + } + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if(neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(ip6_addr_t * ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/mem.c b/app/src/main/jni/badvpn/lwip/src/core/mem.c new file mode 100644 index 0000000..1659a2c --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/mem.c @@ -0,0 +1,659 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + void *ret; + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + return ret; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if(sys_mutex_new(&mem_mutex) != ERR_OK) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc or mem_trim */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/memp.c b/app/src/main/jni/badvpn/lwip/src/core/memp.c new file mode 100644 index 0000000..1323463 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/memp.c @@ -0,0 +1,485 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp_impl.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" +#include "lwip/snmp_structs.h" +#include "lwip/snmp_msg.h" +#include "lwip/dns.h" +#include "netif/ppp_oe.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static +#endif +const u16_t memp_sizes[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +#ifdef LWIP_DEBUG +static const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +#if MEMP_SEPARATE_POOLS + +/** This creates each memory pool. These are named memp_memory_XXX_base (where + * XXX is the name of the pool defined in memp_std.h). + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[]; + */ +#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; +#include "lwip/memp_std.h" + +/** This array holds the base of each memory pool. */ +static u8_t *const memp_bases[] = { +#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base, +#include "lwip/memp_std.h" +}; + +#else /* MEMP_SEPARATE_POOLS */ + +/** This is the actual memory used by the pools (all pools in one big block). */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#endif /* MEMP_SEPARATE_POOLS */ + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm". + */ +static int +memp_sanity(void) +{ + s16_t i; + struct memp *t, *h; + + for (i = 0; i < MEMP_MAX; i++) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +#if defined(LWIP_DEBUG) && MEMP_STATS +static const char * memp_overflow_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) "/"desc, +#include "lwip/memp_std.h" + }; +#endif + +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type]; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_overflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_underflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + +#if !MEMP_SEPARATE_POOLS + memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; +#if MEMP_SEPARATE_POOLS + memp = (struct memp*)memp_bases[i]; +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element_overflow(memp, type); + memp_overflow_check_element_underflow(memp, type); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} + +#endif /* MEMP_MEM_MALLOC */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/netif.c b/app/src/main/jni/badvpn/lwip/src/core/netif.c new file mode 100644 index 0000000..44b7d3f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/netif.c @@ -0,0 +1,912 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +static u8_t netif_num; + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +#if LWIP_HAVE_LOOPIF +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ +static err_t +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF + ip_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); + +#if NO_SYS + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) +{ +#if LWIP_IPV6 + u32_t i; +#endif + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip6_addr_set_zero(&netif->ip6_addr[i]); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /* netif not under DHCPv6 control by default */ + netif->dhcp6 = NULL; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netif_num++; + netif->input = input; + NETIF_SET_HWADDRHINT(netif, NULL); +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ + if (netif == NULL) { + return; + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + snmp_dec_iflist(); + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +int netif_is_named (struct netif *netif, const char name[3]) +{ + u8_t num = name[2] - '0'; + + return (!memcmp(netif->name, name, 2) && netif->num == num); +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip)) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) && + (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->ip_addr), + ip4_addr2_16(&netif->ip_addr), + ip4_addr3_16(&netif->ip_addr), + ip4_addr4_16(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, ip_addr_t *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->gw), + ip4_addr2_16(&netif->gw), + ip4_addr3_16(&netif->gw), + ip4_addr4_16(&netif->gw))); +} + +void netif_set_pretend_tcp (struct netif *netif, u8_t pretend) +{ + if (pretend) { + netif->flags |= NETIF_FLAG_PRETEND_TCP; + } else { + netif->flags &= ~NETIF_FLAG_PRETEND_TCP; + } +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, ip_addr_t *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->netmask), + ip4_addr2_16(&netif->netmask), + ip4_addr3_16(&netif->netmask), + ip4_addr4_16(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } else { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_STATUS_CALLBACK(netif); + + if (netif->flags & NETIF_FLAG_LINK_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + } + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ + NETIF_STATUS_CALLBACK(netif); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + snmp_add_ifoutoctets(stats_if, p->tot_len); + snmp_inc_ifoutucastpkts(stats_if); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((tcpip_callback_fn)netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if (in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if (in != NULL) { + LINK_STATS_INC(link.recv); + snmp_add_ifinoctets(stats_if, in->tot_len); + snmp_inc_ifinucastpkts(stats_if); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while (netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +s8_t +netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +void +netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); + netif->ip6_addr[0].addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } + else { + /* Use hwaddr directly as interface ID. */ + netif->ip6_addr[0].addr[2] = 0; + netif->ip6_addr[0].addr[3] = 0; + + addr_index = 3; + for (i = 0; i < 8; i++) { + if (i == 4) { + addr_index--; + } + netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; +#else + /* Consider address valid. */ + netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + (void)netif; + (void)p; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/pbuf.c b/app/src/main/jni/badvpn/lwip/src/core/pbuf.c new file mode 100644 index 0000000..1e5e53b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/pbuf.c @@ -0,0 +1,1179 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" +#if LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 0; + SYS_ARCH_UNPROTECT(old_level); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Initialize a custom pbuf (already allocated). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + return 1; + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF) { + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /** Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(struct pbuf* p, u16_t offset) +{ + u16_t copy_from = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= copy_from)) { + copy_from -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > copy_from)) { + return ((u8_t*)q->payload)[copy_from]; + } + return 0; +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at wich to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > start)) { + u16_t i; + for(i = 0; i < n; i++) { + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; + } + return 0xffff; +} + +/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for(i = start_offset; i <= max; ) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } else { + i += plus; + } + } + } + return 0xFFFF; +} + +/** Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/app/src/main/jni/badvpn/lwip/src/core/raw.c b/app/src/main/jni/badvpn/lwip/src/core/raw.c new file mode 100644 index 0000000..68b23c6 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/raw.c @@ -0,0 +1,422 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#include + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; +#if LWIP_IPV6 + struct ip6_hdr *ip6hdr; +#endif /* LWIP_IPV6 */ + + + LWIP_UNUSED_ARG(inp); + + iphdr = (struct ip_hdr *)p->payload; +#if LWIP_IPV6 + if (IPH_V(iphdr) == 6) { + ip6hdr = (struct ip6_hdr *)p->payload; + proto = IP6H_NEXTH(ip6hdr); + } + else +#endif /* LWIP_IPV6 */ + { + proto = IPH_PROTO(iphdr); + } + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) +#if LWIP_IPV6 + && !PCB_ISIPV6(pcb) +#endif /* LWIP_IPV6 */ + ) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv.ip4 != NULL) { +#ifndef LWIP_NOASSERT + void* old_payload = p->payload; +#endif + /* the receive callback function did not eat the packet? */ + eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); + if (eaten != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } else { + /* sanity-check that the receive callback did not alter the pbuf */ + LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", + p->payload == old_payload); + } + } + /* no receive callback function was set for this raw PCB */ + } + /* drop the packet */ + } + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t +raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + ipX_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + s16_t header_size; + ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + header_size = ( +#if LWIP_IPV6 + PCB_ISIPV6(pcb) ? IP6_HLEN : +#endif /* LWIP_IPV6 */ + IP_HLEN); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, header_size)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if(pbuf_header(q, -header_size)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); + if (netif == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST +#if LWIP_IPV6 + /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */ + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } + } +#endif /* IP_SOF_BROADCAST */ + + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); +#if LWIP_IPV6 + if (src_ip == NULL) { + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } +#endif /* LWIP_IPV6 */ + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &pcb->local_ip; + } + + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip)); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new(u8_t proto) +{ + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#if LWIP_IPV6 +/** + * Create a RAW PCB for IPv6. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number (next header) of the IPv6 packet payload + * (e.g. IP6_NEXTH_ICMP6) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new_ip6(u8_t proto) +{ + struct raw_pcb *pcb; + pcb = raw_new(proto); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_RAW */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_dec.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_dec.c new file mode 100644 index 0000000..1d56582 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_enc.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_enc.c new file mode 100644 index 0000000..64dfc5f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = (u8_t)length; + } + else + { + /* most significant length octet */ + *msg_ptr = (u8_t)(length >> 8); + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]); + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/mib2.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/mib2.c new file mode 100644 index 0000000..dcd3b62 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/mib2.c @@ -0,0 +1,4146 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "lwip/sys.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10)) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node*)&udp_root, (struct mib_node*)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node*)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 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 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root, + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable, + (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable, + (struct mib_node*)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node*)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node*)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node*)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node*)&sys_tem, + (struct mib_node*)&interfaces, + (struct mib_node*)&at, + (struct mib_node*)&mib2_ip, + (struct mib_node*)&icmp, +#if LWIP_TCP + (struct mib_node*)&tcp, +#endif + (struct mib_node*)&udp, + (struct mib_node*)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n) +{ + u16_t i = n; + while (i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + u8_t i = n; + while(i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + insert = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + insert = 1; + } + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t del = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + del = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + del = 1; + } + } + if (del) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct udp_pcb *npcb; + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + npcb = udp_pcbs; + while ((npcb != NULL)) + { + if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) && + (npcb->local_port == udpidx[4])) + { + bindings++; + } + npcb = npcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy((u8_t*)value, sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t))); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime((u32_t*)value); + } + break; + case 4: /* sysContact */ + ocstrncpy((u8_t*)value, syscontact_ptr, len); + break; + case 5: /* sysName */ + ocstrncpy((u8_t*)value, sysname_ptr, len); + break; + case 6: /* sysLocation */ + ocstrncpy((u8_t*)value, syslocation_ptr, len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid len", len <= 0xff); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr, (u8_t*)value, len); + *syscontact_len_ptr = (u8_t)len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr, (u8_t*)value, len); + *sysname_len_ptr = (u8_t)len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr, (u8_t*)value, len); + *syslocation_len_ptr = (u8_t)len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy((u8_t*)value, (u8_t*)netif->name, len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy((u8_t*)value, netif->hwaddr, len); + break; + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t))); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + LWIP_UNUSED_ARG(len); + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + LWIP_UNUSED_ARG(len); + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + ip_addr_t ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = (s32_t*)value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = IPADDR_BROADCAST & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + ip_addr_t dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + + if (ip_addr_isany(&dest)) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has 0.0.0.0 dest */ + ip_addr_set_zero(dst); + } + else + { + /* netifs have netaddress dest */ + ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask); + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = (s32_t*)value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = (s32_t*)value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = (s32_t*)value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte use 0.0.0.0 mask */ + ip_addr_set_zero(dst); + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t))); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = (s32_t*)value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + ip_addr_t lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + ipX_addr_t ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip); + LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff)); + port = (u16_t)od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !(ipX_addr_cmp(0, &pcb->local_ip, &ip) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + ipX_addr_t *dst = (ipX_addr_t*)value; + ipX_addr_copy(0, *dst, pcb->local_ip); + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */ + u8_t *ptr = (u8_t*)value; + *snmpenableauthentraps_ptr = *ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/mib_structs.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/mib_structs.c new file mode 100644 index 0000000..2f185cb --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/mib_structs.c @@ -0,0 +1,1174 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/memp.h" +#include "lwip/netif.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + s32_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, ip_addr_t *ip) +{ + IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]); +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(ip_addr_t *ip, s32_t *ident) +{ + ident[0] = ip4_addr1(ip); + ident[1] = ip4_addr2(ip); + ident[2] = ip4_addr3(ip); + ident[3] = ip4_addr4(ip); +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + memp_free(MEMP_SNMP_NODE, ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + memp_free(MEMP_SNMP_ROOTNODE, lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (return) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_in.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_in.c new file mode 100644 index 0000000..be940c6 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_in.c @@ -0,0 +1,1453 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/memp.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + +#ifdef SNMP_PRIVATE_MIB_INIT + /* If defined, this must be a function-like define to initialize the + * private MIB after the stack has been initialized. + * The private MIB can also be initialized in tcpip_callback (or after + * the stack is initialized), this define is only for convenience. */ + SNMP_PRIVATE_MIB_INIT(); +#endif /* SNMP_PRIVATE_MIB_INIT */ + + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + /* move names back from outvb to invb */ + int v; + struct snmp_varbind *vbi = msg_ps->invb.head; + struct snmp_varbind *vbo = msg_ps->outvb.head; + for (v=0; vvb_idx; v++) { + vbi->ident_len = vbo->ident_len; + vbo->ident_len = 0; + vbi->ident = vbo->ident; + vbo->ident = NULL; + vbi = vbi->next; + vbo = vbo->next; + } + /* free outvb */ + snmp_varbind_list_free(&msg_ps->outvb); + /* we send invb back */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + /* error index must be 0 for error too big */ + msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) && + (msg_ps->ext_object_def.access & MIB_ACCESS_READ)) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb->value_len = (u8_t)msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if ((object_def.instance != MIB_OBJECT_NONE) && + (object_def.access & MIB_ACCESS_READ)) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb->value_len = (u8_t)object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", + vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + vb->ident = NULL; + vb->ident_len = 0; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + (u8_t)msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access & MIB_ACCESS_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + err_t err_ret; + u16_t payload_len = p->tot_len; + u16_t payload_ofs = 0; + u16_t varbind_ofs = 0; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx == SNMP_CONCURRENT_REQUESTS) + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + return; + } + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + /* Only accept requests and requests without error (be robust) */ + /* Reject response and trap headers or error requests as input! */ + if ((err_ret != ERR_OK) || + ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) || + ((msg_ps->error_status != SNMP_ES_NOERROR) || + (msg_ps->error_index != 0)) ) + { + /* header check failed drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + return; + } + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0)) + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + return; + } + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = (u8_t)len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = (s32_t*)vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = (u32_t*)vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + LWIP_ASSERT("invalid length", len <= 0xff); + vb = snmp_varbind_alloc(&oid, type, (u8_t)len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = (s32_t*)vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH); + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE); + if (vb->ident == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + /* allocate raw bytes for our object value */ + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); + if (vb->ident != NULL) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->value); + } + if (vb->ident != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_out.c b/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_out.c new file mode 100644 index 0000000..fc0807c --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/snmp/msg_out.c @@ -0,0 +1,678 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + ip_addr_t dst_ip; + struct pbuf *p; + u16_t i,tot_len; + err_t err = ERR_OK; + + for (i=0, td = &trap_dst[0]; ienable != 0) && !ip_addr_isany(&td->dip)) + { + /* network order trap destination */ + ip_addr_copy(trap_msg.dip, td->dip); + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + if (dst_if != NULL) { + ip_addr_copy(dst_ip, dst_if->ip_addr); + /* @todo: what about IPv6? */ + trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); + trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); + trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); + trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** send to the TRAP destination */ + udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; + } + } + } + return err; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = (u8_t*)vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/stats.c b/app/src/main/jni/badvpn/lwip/src/core/stats.c new file mode 100644 index 0000000..06fbe0f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/stats.c @@ -0,0 +1,181 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +void stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEMP_STATS + const char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + int i; + for (i = 0; i < MEMP_MAX; i++) { + lwip_stats.memp[i].name = memp_names[i]; + } +#endif /* MEMP_STATS */ +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, const char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP6_FRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + ICMP6_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/app/src/main/jni/badvpn/lwip/src/core/sys.c b/app/src/main/jni/badvpn/lwip/src/core/sys.c new file mode 100644 index 0000000..f177737 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/sys.c @@ -0,0 +1,68 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/sys.h" + +/* Most of the functions defined in sys.h must be implemented in the + * architecture-dependent file sys_arch.c */ + +#if !NO_SYS + +#ifndef sys_msleep +/** + * Sleep for some ms. Timeouts are NOT processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + if (ms > 0) { + sys_sem_t delaysem; + err_t err = sys_sem_new(&delaysem, 0); + if (err == ERR_OK) { + sys_arch_sem_wait(&delaysem, ms); + sys_sem_free(&delaysem); + } + } +} +#endif /* sys_msleep */ + +#endif /* !NO_SYS */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/tcp.c b/app/src/main/jni/badvpn/lwip/src/core/tcp.c new file mode 100644 index 0000000..55496d0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/tcp.c @@ -0,0 +1,1852 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" + +#include + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +const char * const tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +/** Only used for temporary storage. */ +struct tcp_pcb *tcp_tmp_pcb; + +u8_t tcp_active_pcbs_changed; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Called periodically to dispatch TCP timers. + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + err_t err; + + if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, pcb); + } + return ERR_OK; + } + } + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + err = ERR_OK; + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent and acked before close returns. + This can only be valid for sequential APIs, not for the raw API. */ + tcp_output(pcb); + } + return err; +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + int send_rst = reset && (pcb->state != CLOSED); + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + TCP_PCB_REMOVE_ACTIVE(pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + if (send_rst) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + } +} + +/** + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_VAL if bind failed because the PCB is not in a valid state + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* @todo: check accept_any_ip_version */ + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || + ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { + return ERR_USE; + } + } + } + } + } + + pcb->bound_to_netif = 0; + if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr)); + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} + +err_t +tcp_bind_to_netif(struct tcp_pcb *pcb, const char ifname[3]) +{ + LWIP_ERROR("tcp_bind_if: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + /* Check if the interface is already in use */ + for (int i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(struct tcp_pcb *cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + cpcb->bound_to_netif && + !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif))) { + return ERR_USE; + } + } + } + + pcb->bound_to_netif = 1; + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->local_ip); + pcb->local_port = 0; + memcpy(pcb->local_netif, ifname, sizeof(pcb->local_netif)); + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind_to_netif: bind to interface %c%c%c\n", ifname[0], ifname[1], ifname[2])); + return ERR_OK; +} + +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR) && !pcb->have_local_netif) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb)) { + if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->bound_to_netif = pcb->bound_to_netif; + lpcb->local_port = pcb->local_port; + memcpy(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + ip_set_option(lpcb, SOF_ACCEPTCONN); + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; +#if LWIP_IPV6 + PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); + lpcb->accept_any_ip_version = 0; +#endif /* LWIP_IPV6 */ + ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb); + return (struct tcp_pcb *)lpcb; +} + +#if LWIP_IPV6 +/** + * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 + * connections, if the pcb's local address is set to ANY. + */ +struct tcp_pcb * +tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb *lpcb; + + lpcb = tcp_listen_with_backlog(pcb, backlog); + if ((lpcb != NULL) && + ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* The default behavior is to accept connections on either + * IPv4 or IPv6, if not bound. */ + /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ + ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; + } + return lpcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); + pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_port = TCP_LOCAL_PORT_RANGE_START; + } + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_ERROR("tcp_connect: cannot connect pcb bound to netif", !pcb->bound_to_netif, return ERR_VAL); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); + } else { + return ERR_VAL; + } + pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* no local IP address set, yet. */ + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the address as local address of the pcb. */ + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + IP_PCB_IPVER_EQ(cpcb, pcb) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(pcb); + snmp_inc_tcpactiveopens(); + + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) { + ++pcb->rtime; + } + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + } + + /* Check if KEEPALIVE should be sent */ + if(ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + ++pcb_remove; + ++pcb_reset; + } + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_err_fn err_fn; + void *err_arg; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + tcp_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + ++tcp_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while(pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; + pcb->refused_data = NULL; + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ + pcb->refused_data = refused_data; + } + return ERR_OK; +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + pcb->last_timer = tcp_timer_ctr; + + pcb->polltmr = 0; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +#if LWIP_IPV6 +/** + * Creates a new TCP-over-IPv6 protocol control block but doesn't + * place it on any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip6(void) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Used to specify the argument that should be passed callback + * functions. + * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((!lpcb->bound_to_netif && !pcb->bound_to_netif && + (lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb) && + (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) || + (lpcb->bound_to_netif && pcb->bound_to_netif && + !memcmp(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + outif = ipX_route(isipv6, src, dest); +#if LWIP_IPV6 + if (isipv6) { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); + } else +#endif /* LWIP_IPV6 */ + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } + + if (mtu != 0) { + mss_s = mtu - IP_HLEN - TCP_HLEN; +#if LWIP_IPV6 + /* for IPv6, substract the difference in header size */ + mss_s -= (IP6_HLEN - IP_HLEN); +#endif /* LWIP_IPV6 */ + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/tcp_in.c b/app/src/main/jni/badvpn/lwip/src/core/tcp_in.c new file mode 100644 index 0000000..92ee2b2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/tcp_in.c @@ -0,0 +1,1666 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the TCP header) + * @param inp network interface on which this segment was received + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen; + err_t err; +#if CHECKSUM_CHECK_TCP + u16_t chksum; +#endif /* CHECKSUM_CHECK_TCP */ + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + tcphdr = (struct tcp_hdr *)p->payload; + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* Check that TCP header fits in payload */ + if (p->len < sizeof(struct tcp_hdr)) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || + ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { + TCP_STATS_INC(tcp.proterr); + goto dropped; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, + ipX_current_src_addr(), ipX_current_dest_addr()); + if (chksum != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + chksum)); + tcp_debug_print(tcphdr); + TCP_STATS_INC(tcp.chkerr); + goto dropped; + } +#endif /* CHECKSUM_CHECK_TCP */ + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + struct tcp_pcb_listen *netif_pcb = NULL; + struct tcp_pcb *netif_pcb_prev; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->bound_to_netif) { + if (IP_PCB_IPVER_INPUT_MATCH(lpcb) && netif_is_named(inp, lpcb->local_netif)) { + netif_pcb = lpcb; + netif_pcb_prev = prev; + } + } + else if (lpcb->local_port == tcphdr->dest) { +#if LWIP_IPV6 + if (lpcb->accept_any_ip_version) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; +#endif /* SO_REUSE */ + } else +#endif /* LWIP_IPV6 */ + if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { + if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; + #endif /* SO_REUSE */ + } + } + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb == NULL && netif_pcb) { + lpcb = netif_pcb; + prev = netif_pcb_prev; + } + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + if (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + goto aborted; + } + } + tcp_input_pcb = pcb; + err = tcp_process(pcb); + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + + if (recv_data != NULL) { + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); + tcp_abort(pcb); + goto aborted; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + } + + tcp_input_pcb = NULL; + /* Try to send something out. */ + tcp_output(pcb); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ +#if LWIP_IPV6 + PCB_ISIPV6(npcb) = ip_current_is_v6(); +#endif /* LWIP_IPV6 */ + ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); + ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); + npcb->bound_to_netif = pcb->bound_to_netif; + npcb->local_port = tcphdr->dest; + memcpy(npcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + npcb->snd_wnd = tcphdr->wnd; + npcb->snd_wnd_max = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG_ACTIVE(npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, + &npcb->remote_ip, PCB_ISIPV6(npcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) { + tcp_abandon(npcb, 0); + return rc; + } + return tcp_output(npcb); + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + return tcp_output(pcb); + } + return ERR_OK; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wnd_max = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, + PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < tcphdr->wnd) { + pcb->snd_wnd_max = tcphdr->wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data + * 2) length of received packet is zero (i.e. no payload) + * 3) the advertised window hasn't changed + * 4) There is outstanding unacknowledged data (retransmission timer running) + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) { + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) { + ++pcb->dupacks; + } + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else + pcb->rtime = 0; + + pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further unless the pcb already received a FIN. + (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){ + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg); + + if (tcplen > pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno; + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#endif /* TCP_QUEUE_OOSEQ */ + } + } else { + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u16_t c, max_c; + u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { + opt = opts[c]; + switch (opt) { + case 0x00: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ + ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; + break; +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/tcp_out.c b/app/src/main/jni/badvpn/lwip/src/core/tcp_out.c new file mode 100644 index 0000000..b9fc339 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/tcp_out.c @@ -0,0 +1,1499 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#include + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf * +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + pcb->flags |= TF_FIN; + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg * +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that willo enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + * @param + */ +#if TCP_OVERSIZE +static struct pbuf * +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + /* always create MSS-sized pbufs */ + alloc = max_length; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", + len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + space = mss_local - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); + seg = last_unsent; + oversize_used = oversize < len ? oversize : len; + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = space < len - pos ? space : len - pos; + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left += oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } else { + /* Data is not copied */ + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + concat_p->payload = (u8_t*)arg + pos; + } + + pos += seglen; + queuelen += pbuf_clen(concat_p); + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = mss_local - optlen; + u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + p2->payload = (u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + * @param optdata pointer to TCP options, or NULL. + * @param optlen length of TCP options in bytes. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* tcp_enqueue_flags is always called with either SYN or FIN in flags. + * We need one available snd_buf byte to do that. + * This means we can't send FIN while snd_buf==0. A better fix would be to + * not include SYN and FIN sequence numbers in the snd_buf count. */ + if (pcb->snd_buf == 0) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + pcb->snd_buf--; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + u8_t optlen = 0; +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb); + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? */ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + pcb->flags &= ~TF_NAGLEMEMERR; + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + u32_t *opts; + + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + return; + } + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ +#endif /* TCP_CHECKSUM_ON_COPY */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + pcb->tos, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + /* last unsent hasn't changed, no need to reset unsent_oversize */ + + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) { + pcb->ssthresh = pcb->snd_wnd / 2; + } else { + pcb->ssthresh = pcb->cwnd / 2; + } + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; +#if CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* CHECKSUM_GEN_TCP */ + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } +#if CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; + + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) { + seg = pcb->unsent; + } + if(seg == NULL) { + return; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/timers.c b/app/src/main/jni/badvpn/lwip/src/core/timers.c new file mode 100644 index 0000000..c8ead4e --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/timers.c @@ -0,0 +1,546 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/tcp_impl.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_now(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (next_timeout == NULL) { + next_timeout = timeout; + return; + } + + if (next_timeout->time > msecs) { + next_timeout->time -= msecs; + timeout->next = next_timeout; + next_timeout = timeout; + } else { + for(t = next_timeout; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'handler' in the list of timeouts. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout(sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + + if (next_timeout == NULL) { + return; + } + + for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + next_timeout = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS + +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = sys_now(); +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + again: + if (!next_timeout) { + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (next_timeout->time > 0) { + time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = next_timeout; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < next_timeout->time) { + next_timeout->time -= time_needed; + } else { + next_timeout->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/app/src/main/jni/badvpn/lwip/src/core/udp.c b/app/src/main/jni/badvpn/lwip/src/core/udp.c new file mode 100644 index 0000000..ac0e56d --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/core/udp.c @@ -0,0 +1,1151 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/icmp6.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +#if 0 + struct udp_pcb *ipcb = udp_pcbs; + while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == udp_port) { + /* port is already used by another udp_pcb */ + udp_port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + return 0; + } + return udp_port; +#endif +} + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + u8_t for_us; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ +#if LWIP_IPV6 + broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#else /* LWIP_IPV6 */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#endif /* LWIP_IPV6 */ + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src + (no need to check for IPv6 since the dhcp struct always uses IPv4) */ + if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { + pcb = inp->dhcp->pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if (pcb->local_port == dest) { + if ( +#if LWIP_IPV6 + ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && + (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(pcb) && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(pcb, SOF_BROADCAST) && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr()); + } else +#endif /* LWIP_IPV6 */ + { + for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); + } + } + if (for_us) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP +#if LWIP_UDPLITE + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + goto chkerr; + } + } + if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } else +#endif /* LWIP_UDPLITE */ + { + if (udphdr->chksum != 0) { + if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, + ipX_current_src_addr(), + ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } + } +#endif /* CHECKSUM_CHECK_UDP */ + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); +#if SO_REUSE && SO_REUSE_RXTOALL + if ((broadcast || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && + ip_get_option(pcb, SOF_REUSEADDR)) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && +#if LWIP_IPV6 + ((PCB_ISIPV6(mpcb) && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(mpcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + /* pass a copy of the packet to all local matches */ + if (mpcb->recv.ip4 != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header(p, hdrs_len); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -hdrs_len); +#if LWIP_IPV6 + if (PCB_ISIPV6(mpcb)) { + mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -hdrs_len); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv.ip4 != NULL) { + /* now the recv function is responsible for freeing p */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP || LWIP_ICMP6 + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { + /* move payload pointer back to ip header */ + pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); + icmp_port_unreach(ip_current_is_v6(), p); + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +/** Same as udp_send() but with checksum + */ +err_t +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +err_t +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct netif *netif; + ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + +#if LWIP_IPV6 || LWIP_IGMP + if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { + /* For multicast, find a netif based on source address. */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { +#if LWIP_IGMP + dst_ip_route = ip_2_ipX(&pcb->multicast_ip); +#endif /* LWIP_IGMP */ + } + } +#endif /* LWIP_IPV6 || LWIP_IGMP */ + + /* find the outgoing network interface for this packet */ + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct udp_hdr *udphdr; + ip_addr_t *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + !PCB_ISIPV6(pcb) && +#endif /* LWIP_IPV6 */ + ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if LWIP_IGMP + if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && +#if LWIP_IPV6 + ( +#if LWIP_IPV6_MLD + (PCB_ISIPV6(pcb) && + ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || +#endif /* LWIP_IPV6_MLD */ + (!PCB_ISIPV6(pcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(dst_ip)))) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + + /* PCB local address is IP_ANY_ADDR? */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { + src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); + if (src_ip == NULL) { + /* No suitable source address was found. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + src_ip = ipX_2_ip(&pcb->local_ip); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip))) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = ipX_2_ip(&(pcb->local_ip)); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, + q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_GEN_UDP */ + + ip_proto = IP_PROTO_UDPLITE; + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + /* Checksum is mandatory over IPv6. */ + if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, + ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + ip_proto = IP_PROTO_UDP; + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(ipaddr)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* By default, we don't allow to bind to a port that any other udp + PCB is alread bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { +#else /* SO_REUSE */ + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + else { +#endif /* SO_REUSE */ + if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || + ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { + port = udp_new_port(); + if (port == 0) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); + return ERR_OK; +} + +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + struct netif *netif; + + if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", + ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); + } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + ipX_addr_set_any(0, &pcb->local_ip); + } + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if LWIP_IPV6 +/** + * Create a UDP PCB for IPv6. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip6(void) +{ + struct udp_pcb *pcb; + pcb = udp_new(); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h new file mode 100644 index 0000000..e62b72e --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h @@ -0,0 +1,118 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +#define autoip_init() /* Compatibility define, no init needed. */ + +/** Set a struct autoip allocated by the application to work with */ +void autoip_set_struct(struct netif *netif, struct autoip *autoip); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +/** Handle a possible change in the network configuration */ +void autoip_network_changed(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h new file mode 100644 index 0000000..fa893b6 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +/** This is the standard ICMP header only that the u32_t data + * is splitted to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +void icmp_input(struct pbuf *p, struct netif *inp); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#if (LWIP_IPV6 && LWIP_ICMP6) +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h new file mode 100644 index 0000000..8aabac2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions, these are passed to a netif's + * igmp_mac_filter callback function. */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + + +/** + * igmp group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ +struct igmp_group { + /** next link */ + struct igmp_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting, negative is OFF */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/* Prototypes */ +void igmp_init(void); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest); +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +void igmp_tmr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h new file mode 100644 index 0000000..7bff49b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IN_CLASSA(a) IP_CLASSA(a) +#define IN_CLASSA_NET IP_CLASSA_NET +#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT +#define IN_CLASSA_HOST IP_CLASSA_HOST +#define IN_CLASSA_MAX IP_CLASSA_MAX + +#define IN_CLASSB(b) IP_CLASSB(b) +#define IN_CLASSB_NET IP_CLASSB_NET +#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT +#define IN_CLASSB_HOST IP_CLASSB_HOST +#define IN_CLASSB_MAX IP_CLASSB_MAX + +#define IN_CLASSC(c) IP_CLASSC(c) +#define IN_CLASSC_NET IP_CLASSC_NET +#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT +#define IN_CLASSC_HOST IP_CLASSC_HOST +#define IN_CLASSC_MAX IP_CLASSC_MAX + +#define IN_CLASSD(d) IP_CLASSD(d) +#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */ +#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */ +#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */ +#define IN_CLASSD_MAX IP_CLASSD_MAX + +#define IN_MULTICAST(a) IP_MULTICAST(a) + +#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a) +#define IN_BADCLASS(a) IP_BADCLASS(a) + +#define IN_LOOPBACKNET IP_LOOPBACKNET + +#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) +#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) +/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */ +#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr)) + +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr) +#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr)) +#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h new file mode 100644 index 0000000..04b5b8a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_H__ +#define __LWIP_IP4_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND LWIP_IGMP + +#define IP_HLEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_IGMP 2 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length */ + PACK_STRUCT_FIELD(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FIELD(u8_t _tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* dont fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FIELD(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FIELD(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip_addr_p_t src); + PACK_STRUCT_FIELD(ip_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IPH_V(hdr) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) ((hdr)->_ttl) +#define IPH_PROTO(hdr) ((hdr)->_proto) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + + +#define ip_init() /* Compatibility define, no init needed. */ +struct netif *ip_route(ip_addr_t *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#endif /* IP_OPTIONS_SEND */ + +#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h new file mode 100644 index 0000000..b05ae53 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_ADDR_H__ +#define __LWIP_IP4_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the aligned version of ip_addr_t, + used as local variable, on the stack, etc. */ +struct ip_addr { + u32_t addr; +}; + +/* This is the packed version of ip_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr_packed { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip_addr_t as well as on ip_addr_p_t. */ +typedef struct ip_addr ip_addr_t; +typedef struct ip_addr_packed ip_addr_p_t; + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Forward declaration to not include netif.h */ +struct netif; + +extern const ip_addr_t ip_addr_any; +extern const ip_addr_t ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IP_CLASSA_NET 0xff000000 +#define IP_CLASSA_NSHIFT 24 +#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET) +#define IP_CLASSA_MAX 128 + +#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IP_CLASSB_NET 0xffff0000 +#define IP_CLASSB_NSHIFT 16 +#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET) +#define IP_CLASSB_MAX 65536 + +#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IP_CLASSC_NET 0xffffff00 +#define IP_CLASSC_NSHIFT 8 +#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET) + +#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IP_MULTICAST(a) IP_CLASSD(a) + +#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IP_LOOPBACKNET 127 /* official! */ + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IP address given by the four byte-parts. + Little-endian version that prevents the use of htonl. */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t)) +#endif + +/** Copy IP address - faster than ip_addr_set: no NULL check */ +#define ip_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip_addr_set(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for htonl()) */ +#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr)) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY) + +#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif)) +u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif); + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0)) + +/* Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0]) +#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1]) +#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2]) +#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3]) +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ipaddr_aton(const char *cp, ip_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr); +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h new file mode 100644 index 0000000..47eca9f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef __LWIP_IP_FRAG_H__ +#define __LWIP_IP_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ +#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h new file mode 100644 index 0000000..4b905c5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_IP6_DHCP6_H__ +#define __LWIP_IP6_DHCP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*TODO: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* __LWIP_IP6_DHCP6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h new file mode 100644 index 0000000..e7f412b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ETHIP6_H__ +#define __LWIP_ETHIP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* __LWIP_ETHIP6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h new file mode 100644 index 0000000..74bfdbe --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h @@ -0,0 +1,152 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_ICMP6_H__ +#define __LWIP_ICMP6_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum icmp6_type { + ICMP6_TYPE_DUR = 1, /* Destination unreachable */ + ICMP6_TYPE_PTB = 2, /* Packet too big */ + ICMP6_TYPE_TE = 3, /* Time exceeded */ + ICMP6_TYPE_PP = 4, /* Parameter problem */ + ICMP6_TYPE_PE1 = 100, /* Private experimentation */ + ICMP6_TYPE_PE2 = 101, /* Private experimentation */ + ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */ + + ICMP6_TYPE_EREQ = 128, /* Echo request */ + ICMP6_TYPE_EREP = 129, /* Echo reply */ + ICMP6_TYPE_MLQ = 130, /* Multicast listener query */ + ICMP6_TYPE_MLR = 131, /* Multicast listener report */ + ICMP6_TYPE_MLD = 132, /* Multicast listener done */ + ICMP6_TYPE_RS = 133, /* Router solicitation */ + ICMP6_TYPE_RA = 134, /* Router advertisement */ + ICMP6_TYPE_NS = 135, /* Neighbor solicitation */ + ICMP6_TYPE_NA = 136, /* Neighbor advertisement */ + ICMP6_TYPE_RD = 137, /* Redirect */ + ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */ + ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */ + ICMP6_TYPE_MRT = 153, /* Multicast router termination */ + ICMP6_TYPE_PE3 = 200, /* Private experimentation */ + ICMP6_TYPE_PE4 = 201, /* Private experimentation */ + ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */ +}; + +enum icmp6_dur_code { + ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */ + ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */ + ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */ + ICMP6_DUR_ADDRESS = 3, /* Address unreachable */ + ICMP6_DUR_PORT = 4, /* Port unreachable */ + ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */ + ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */ +}; + +enum icmp6_te_code { + ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */ + ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */ +}; + +enum icmp6_pp_code { + ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */ + ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */ + ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */ +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +void icmp6_input(struct pbuf *p, struct netif *inp); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* __LWIP_ICMP6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h new file mode 100644 index 0000000..dbf98df --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h @@ -0,0 +1,92 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_INET6_H__ +#define __LWIP_INET6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in6_addr { + union { + u8_t u8_addr[16]; + u32_t u32_addr[4]; + } un; +#define s6_addr un.u32_addr +}; + +#define IN6ADDR_ANY_INIT {0,0,0,0} +#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)} + + +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_INET6_H__ */ + diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h new file mode 100644 index 0000000..b199c95 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h @@ -0,0 +1,197 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_H__ +#define __LWIP_IP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + + +/* The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /* version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /* payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* hop limit */ + PACK_STRUCT_FIELD(u8_t _hoplim); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip6_addr_p_t src); + PACK_STRUCT_FIELD(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* header length */ + PACK_STRUCT_FIELD(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FIELD(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FIELD(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FIELD(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FIELD(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FIELD(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (htonl(((v) << 28) | ((tc) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + + +#define ip6_init() /* TODO should we init current addresses and header pointer? */ +struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest); +ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ipX(netif, dest) (((netif) != NULL) ? \ + ip6_2_ipX(ip6_select_source_address(netif, dest)) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h new file mode 100644 index 0000000..89b5b81 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h @@ -0,0 +1,286 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_ADDR_H__ +#define __LWIP_IP6_ADDR_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/* This is the packed version of ip6_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_addr_packed { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip6_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip6_addr_t as well as on ip6_addr_p_t. */ +typedef struct ip6_addr ip6_addr_t; +typedef struct ip6_addr_packed ip6_addr_p_t; + + +/** IP6_ADDR_ANY can be used as a fixed IPv6 address + * for the wildcard + */ +extern const ip6_addr_t ip6_addr_any; +#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any) + + + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IPv6 partial address given by byte-parts. */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IPv6 partial address given by byte-parts. +Little-endian version, stored in network order (no htonl). */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff) +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff) +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff) +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3])) & 0xffff) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : htonl((src)->addr[3]);}while(0) + + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \ + (((ip6addr)->addr[0] == 0) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == 0) && \ + ((ip6addr)->addr[3] == 0))) + + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* TODO define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = htonl(0xff000000UL | (htonl(if_id) & 0x00ffffffUL));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == htonl(0xff000000UL | (htonl((sn_addr)->addr[3]) & 0x00ffffffUL)))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x50 + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F, \ + ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_ADDR_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h new file mode 100644 index 0000000..75898b8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h @@ -0,0 +1,102 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_FRAG_H__ +#define __LWIP_IP6_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/* The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr * iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf * ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP6_FRAG_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h new file mode 100644 index 0000000..abd86e5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h @@ -0,0 +1,118 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_MLD6_H__ +#define __LWIP_MLD6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct mld_group { + /** next link */ + struct mld_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip6_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +/* MAC Filter Actions, these are passed to a netif's + * mld_mac_filter callback function. */ +#define MLD6_DEL_MAC_FILTER 0 +#define MLD6_ADD_MAC_FILTER 1 + + +#define mld6_init() /* TODO should we init tables? */ +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); +err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* __LWIP_MLD6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h new file mode 100644 index 0000000..28636e8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h @@ -0,0 +1,369 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ND6_H__ +#define __LWIP_ND6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct for tables. */ +struct nd6_neighbor_cache_entry { + ip6_addr_t next_hop_address; + struct netif * netif; + u8_t lladdr[NETIF_MAX_HWADDR_LEN]; + /*u32_t pmtu;*/ +#if LWIP_ND6_QUEUEING + /** Pointer to queue of pending outgoing packets on this entry. */ + struct nd6_q_entry *q; +#else /* LWIP_ND6_QUEUEING */ + /** Pointer to a single pending outgoing packet on this entry. */ + struct pbuf *q; +#endif /* LWIP_ND6_QUEUEING */ + u8_t state; + u8_t isrouter; + union { + u32_t reachable_time; + u32_t delay_time; + u32_t probes_sent; + u32_t stale_time; + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u32_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif * netif; + u32_t invalidation_timer; +#if LWIP_IPV6_AUTOCONFIG + u8_t flags; +#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04 +#endif /* LWIP_IPV6_AUTOCONFIG */ +}; + +struct nd6_router_list_entry { + struct nd6_neighbor_cache_entry * neighbor_entry; + u32_t invalidation_timer; + u8_t flags; +}; + + +enum nd6_neighbor_cache_entry_state { + ND6_NO_ENTRY = 0, + ND6_INCOMPLETE, + ND6_REACHABLE, + ND6_STALE, + ND6_DELAY, + ND6_PROBE +}; + +#if LWIP_ND6_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct nd6_q_entry { + struct nd6_q_entry *next; + struct pbuf *p; +}; +#endif /* LWIP_ND6_QUEUEING */ + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u8_t reserved[3]); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t current_hop_limit); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + PACK_STRUCT_FIELD(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FIELD(u8_t reserved2[3]); + PACK_STRUCT_FIELD(u8_t site_prefix_length); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FIELD(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* the possible states of an IP address */ +#define IP6_ADDRESS_STATE_INVALID (0) +#define IP6_ADDRESS_STATE_VALID (0x4) +#define IP6_ADDRESS_STATE_PREFERRED (0x5) /* includes valid */ +#define IP6_ADDRESS_STATE_DEPRECATED (0x6) /* includes valid */ +#define IP6_ADDRESS_STATE_TENTATIV (0x8) + +/** 1 second period */ +#define ND6_TMR_INTERVAL 1000 + +/* Router tables. */ +/* TODO make these static? and entries accessible through API? */ +extern struct nd6_neighbor_cache_entry neighbor_cache[]; +extern struct nd6_destination_cache_entry destination_cache[]; +extern struct nd6_prefix_list_entry prefix_list[]; +extern struct nd6_router_list_entry default_router_list[]; + +/* Default values, can be updated by a RA message. */ +extern u32_t reachable_time; +extern u32_t retrans_timer; + +#define nd6_init() /* TODO should we init tables? */ +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif); +s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif); +u16_t nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif); +err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p); +#if LWIP_ND6_TCP_REACHABILITY_HINTS +void nd6_reachability_hint(ip6_addr_t * ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_ND6_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/api.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/api.h new file mode 100644 index 0000000..ac58eeb --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/api.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_H__ +#define __LWIP_API_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write (u8_t) */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 +#define NETCONN_DONTBLOCK 0x04 + +/* Flags for struct netconn.flags (u8_t) */ +/** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ +#define NETCONN_FLAG_WRITE_DELAYED 0x01 +/** Should this netconn avoid blocking? */ +#define NETCONN_FLAG_NON_BLOCKING 0x02 +/** Was the last connect action a non-blocking one? */ +#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04 +/** If this is set, a TCP netconn must call netconn_recved() to update + the TCP receive window (done automatically if not set). */ +#define NETCONN_FLAG_NO_AUTO_RECVED 0x08 +/** If a nonblocking write has been rejected before, poll_tcp needs to + check if the netconn is writable again */ +#define NETCONN_FLAG_CHECK_WRITESPACE 0x10 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ + +/** Protocol family and type of the netconn */ +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM = 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ +}; + +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +/** Use to inform the callback function about changes */ +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS, + NETCONN_EVT_ERROR +}; + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; +struct api_msg_msg; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t last_err; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; +#if LWIP_TCP + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; +#endif /* LWIP_TCP */ + /** only used for socket layer */ +#if LWIP_SOCKET + int socket; +#endif /* LWIP_SOCKET */ +#if LWIP_SO_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox + not used for TCP: adjust TCP_WND instead! */ + int recv_bufsize; + /** number of bytes currently in recvmbox to be received, + tested against recv_bufsize to limit bytes on recvmbox + for UDP and RAW, used for FIONREAD */ + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ + u8_t flags; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. + Also used during connect and close. */ + struct api_msg_msg *current_msg; +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/** Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/** Set conn->last_err to err but don't overwrite fatal errors */ +#define NETCONN_SET_SAFE_ERR(conn, err) do { \ + SYS_ARCH_DECL_PROTECT(lev); \ + SYS_ARCH_PROTECT(lev); \ + if (!ERR_IS_FATAL((conn)->last_err)) { \ + (conn)->last_err = err; \ + } \ + SYS_ARCH_UNPROTECT(lev); \ +} while(0); + +/* Network connection functions: */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, + u16_t *port, u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +void netconn_recved(struct netconn *conn, u32_t length); +err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + ip_addr_t *addr, u16_t port); +err_t netconn_send(struct netconn *conn, struct netbuf *buf); +err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, + ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + +#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\ + ERR_VAL) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ + +#define netconn_err(conn) ((conn)->last_err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +/** Set the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_set_nonblocking(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0) +/** Get the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0) + +/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_set_noautorecved(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0) +/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) + +#if LWIP_SO_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO +/** Set the receive timeout in milliseconds */ +#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) +/** Get the receive timeout in milliseconds */ +#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout) +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF +/** Set the receive buffer in bytes */ +#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize)) +/** Get the receive buffer in bytes */ +#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) +#endif /* LWIP_SO_RCVBUF*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h new file mode 100644 index 0000000..8268036 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_MSG_H__ +#define __LWIP_API_MSG_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For the netconn API, these values are use as a bitmask! */ +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** The return value of the function executed in tcpip_thread. */ + err_t err; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for lwip_netconn_do_send */ + struct netbuf *b; + /** used for lwip_netconn_do_newconn */ + struct { + u8_t proto; + } n; + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ + struct { + ip_addr_t *ipaddr; + u16_t port; + } bc; + /** used for lwip_netconn_do_getaddr */ + struct { + ipX_addr_t *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for lwip_netconn_do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } w; + /** used for lwip_netconn_do_recv */ + struct { + u32_t len; + } r; + /** used for lwip_netconn_do_close (/shutdown) */ + struct { + u8_t shut; + } sd; +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ + struct { + ipX_addr_t *multiaddr; + ipX_addr_t *netif_addr; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +}; + +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ +struct api_msg { + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ + struct api_msg_msg msg; +}; + +#if LWIP_DNS +/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + ip_addr_t *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t *sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void lwip_netconn_do_newconn ( struct api_msg_msg *msg); +void lwip_netconn_do_delconn ( struct api_msg_msg *msg); +void lwip_netconn_do_bind ( struct api_msg_msg *msg); +void lwip_netconn_do_connect ( struct api_msg_msg *msg); +void lwip_netconn_do_disconnect ( struct api_msg_msg *msg); +void lwip_netconn_do_listen ( struct api_msg_msg *msg); +void lwip_netconn_do_send ( struct api_msg_msg *msg); +void lwip_netconn_do_recv ( struct api_msg_msg *msg); +void lwip_netconn_do_write ( struct api_msg_msg *msg); +void lwip_netconn_do_getaddr ( struct api_msg_msg *msg); +void lwip_netconn_do_close ( struct api_msg_msg *msg); +void lwip_netconn_do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +void lwip_netconn_do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_MSG_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/arch.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/arch.h new file mode 100644 index 0000000..101a753 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/arch.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ARCH_H__ +#define __LWIP_ARCH_H__ + +#include "arch/cc.h" + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ +/** Temporary upgrade helper: define format string for u8_t as hex if not + defined in cc.h */ +#ifndef X8_F +#define X8_F "02x" +#endif /* X8_F */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/debug.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/debug.h new file mode 100644 index 0000000..0fe0413 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/debug.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEBUG_H__ +#define __LWIP_DEBUG_H__ + +#include "lwip/arch.h" +#include "lwip/opt.h" + +/** lower two bits indicate debug level + * - 0 all + * - 1 warning + * - 2 serious + * - 3 severe + */ +#define LWIP_DBG_LEVEL_ALL 0x00 +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */ +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U + +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +#ifdef LWIP_DEBUG +/** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* __LWIP_DEBUG_H__ */ + diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/def.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/def.h new file mode 100644 index 0000000..73a1b56 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/def.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEF_H__ +#define __LWIP_DEF_H__ + +/* arch.h might define NULL already */ +#include "lwip/arch.h" +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* Endianess-optimized shifting of two u8_t to create one u16_t */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define LWIP_MAKE_U16(a, b) ((a << 8) | b) +#else +#define LWIP_MAKE_U16(a, b) ((b << 8) | a) +#endif + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#ifndef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#define htons(x) lwip_htons(x) +#define ntohs(x) lwip_ntohs(x) +#define htonl(x) lwip_htonl(x) +#define ntohl(x) lwip_ntohl(x) +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) (x) +#define lwip_ntohs(x) (x) +#define lwip_htonl(x) (x) +#define lwip_ntohl(x) (x) +#define PP_HTONS(x) (x) +#define PP_NTOHS(x) (x) +#define PP_HTONL(x) (x) +#define PP_NTOHL(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#if LWIP_PLATFORM_BYTESWAP +#define lwip_htons(x) LWIP_PLATFORM_HTONS(x) +#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x) +#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x) +#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t lwip_htons(u16_t x); +u16_t lwip_ntohs(u16_t x); +u32_t lwip_htonl(u32_t x); +u32_t lwip_ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & 0xff) << 24) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_DEF_H__ */ + diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h new file mode 100644 index 0000000..32d9338 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h @@ -0,0 +1,242 @@ +/** @file + */ + +#ifndef __LWIP_DHCP_H__ +#define __LWIP_DHCP_H__ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_LEN 128U + +struct dhcp +{ + /** transaction identifier of last sent request */ + u32_t xid; + /** our connection to the DHCP server */ + struct udp_pcb *pcb; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif + u8_t subnet_mask_given; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */ + ip_addr_t offered_ip_addr; + ip_addr_t offered_sn_mask; + ip_addr_t offered_gw_addr; + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ + /* @todo: LWIP_DHCP_BOOTP_FILE configuration option? + integrate with possible TFTP-client for booting? */ +#if LWIP_DHCP_BOOTP_FILE + ip_addr_t offered_si_addr; + char boot_file_name[DHCP_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ +}; + +/* MUST be compiled with "pack structs" or equivalent! */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FIELD(u8_t op); + PACK_STRUCT_FIELD(u8_t htype); + PACK_STRUCT_FIELD(u8_t hlen); + PACK_STRUCT_FIELD(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(ip_addr_p_t ciaddr); + PACK_STRUCT_FIELD(ip_addr_p_t yiaddr); + PACK_STRUCT_FIELD(ip_addr_p_t siaddr); + PACK_STRUCT_FIELD(ip_addr_p_t giaddr); + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0) +void dhcp_cleanup(struct netif *netif); +/** start DHCP configuration */ +err_t dhcp_start(struct netif *netif); +/** enforce early lease renewal (not needed normally)*/ +err_t dhcp_renew(struct netif *netif); +/** release the DHCP lease, usually called before dhcp_stop()*/ +err_t dhcp_release(struct netif *netif); +/** stop DHCP configuration */ +void dhcp_stop(struct netif *netif); +/** inform server of our manual IP address */ +void dhcp_inform(struct netif *netif); +/** Handle a possible change in the network configuration */ +void dhcp_network_changed(struct netif *netif); + +/** if enabled, check whether the offered IP address is not in use, using ARP */ +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr); +#endif + +/** to be called every minute */ +void dhcp_coarse_tmr(void); +/** to be called every half second */ +void dhcp_fine_tmr(void); + +/** DHCP message item offsets and length */ +#define DHCP_OP_OFS 0 +#define DHCP_HTYPE_OFS 1 +#define DHCP_HLEN_OFS 2 +#define DHCP_HOPS_OFS 3 +#define DHCP_XID_OFS 4 +#define DHCP_SECS_OFS 8 +#define DHCP_FLAGS_OFS 10 +#define DHCP_CIADDR_OFS 12 +#define DHCP_YIADDR_OFS 16 +#define DHCP_SIADDR_OFS 20 +#define DHCP_GIADDR_OFS 24 +#define DHCP_CHADDR_OFS 28 +#define DHCP_SNAME_OFS 44 +#define DHCP_FILE_OFS 108 +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS DHCP_MSG_LEN +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#define DHCP_OFF 0 +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 +/** not yet implemented #define DHCP_RELEASING 11 */ +#define DHCP_BACKING_OFF 12 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/** DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/** BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_END 255 + +/** DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/** possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*__LWIP_DHCP_H__*/ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/dns.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/dns.h new file mode 100644 index 0000000..6c7d9b0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/dns.h @@ -0,0 +1,124 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/* The size used for the next line is rather a hack, but it prevents including socket.h in all files + that include memp.h, and that would possibly break portability (since socket.h defines some types + and constants possibly already define by the OS). + Calculation rule: + sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */ +#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1) + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + ip_addr_t addr; + struct local_hostlist_entry *next; +}; +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN +#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH +#endif +#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1)) +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); + +void dns_init(void); +void dns_tmr(void); +void dns_setserver(u8_t numdns, ip_addr_t *dnsserver); +ip_addr_t dns_getserver(u8_t numdns); +err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg); + +#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const ip_addr_t *addr); +err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/err.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/err.h new file mode 100644 index 0000000..ac90772 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/err.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ERR_H__ +#define __LWIP_ERR_H__ + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ +typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ + +/* Definitions for error constants. */ + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ +#define ERR_INPROGRESS -5 /* Operation in progress */ +#define ERR_VAL -6 /* Illegal value. */ +#define ERR_WOULDBLOCK -7 /* Operation would block. */ +#define ERR_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) + +#define ERR_ABRT -10 /* Connection aborted. */ +#define ERR_RST -11 /* Connection reset. */ +#define ERR_CLSD -12 /* Connection closed. */ +#define ERR_CONN -13 /* Not connected. */ + +#define ERR_ARG -14 /* Illegal argument. */ + +#define ERR_IF -15 /* Low-level netif error */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h new file mode 100644 index 0000000..e168788 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +#ifndef LWIP_CHKSUM_COPY +#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +#ifndef LWIP_CHKSUM_COPY_ALGORITHM +#define LWIP_CHKSUM_COPY_ALGORITHM 1 +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +#endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +#define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest); + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest))) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest))) + +#else /* LWIP_IPV6 */ + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + inet_chksum_pseudo(p, proto, proto_len, src, dest) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, dest) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/init.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/init.h new file mode 100644 index 0000000..4e2e285 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 1U +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 4U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 1U +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC 0U + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255U +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */ +#define LWIP_RC_DEVELOPMENT 0U + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/** Provides the version of the stack */ +#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \ + LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC) + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/ip.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/ip.h new file mode 100644 index 0000000..a0cd1d4 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/ip.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +#if LWIP_IPV6 +#define IP_PCB_ISIPV6_MEMBER u8_t isipv6; +#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6) +#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \ + ((pcb)->isipv6 != 0) : \ + ((pcb)->isipv6 == 0)) +#define PCB_ISIPV6(pcb) ((pcb)->isipv6) +#else +#define IP_PCB_ISIPV6_MEMBER +#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1 +#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1 +#define PCB_ISIPV6(pcb) 0 +#endif /* LWIP_IPV6 */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + IP_PCB_ISIPV6_MEMBER \ + /* ip addresses in network byte order */ \ + ipX_addr_t local_ip; \ + ipX_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER 0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) + +/* Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** The interface that provided the packet for the current callback invocation. */ + struct netif *current_netif; + /** Header of the input packet currently being processed. */ + const struct ip_hdr *current_ip4_header; +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + const struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ipX_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ipX_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (ip_data.current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (ip_data.current_ip4_header) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV6 +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() (ip_data.current_ip6_header) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ipX_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip_current_header())) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \ + (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len())) + +/** Set an IP_PCB to IPv6 (IPv4 is the default) */ +#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0) + +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest)) + +#else /* LWIP_IPV6 */ + +/** Always returns FALSE when only supporting IPv4 */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip_current_header()) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if LWIP_IPV6 +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ((isipv6) ? \ + ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \ + ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto)) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ((isipv6) ? \ + ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint)) +#define ipX_route(isipv6, src, dest) \ + ((isipv6) ? \ + ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \ + ip_route(ipX_2_ip(dest))) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ((isipv6) ? \ + ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \ + ip_netif_get_local_ipX(netif)) +#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p)) +#else /* LWIP_IPV6 */ +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ip_output(p, src, dest, ttl, tos, proto) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ipX_route(isipv6, src, dest) \ + ip_route(ipX_2_ip(dest)) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ip_netif_get_local_ipX(netif) +#define ipX_debug_print(is_ipv6, p) ip_debug_print(p) +#endif /* LWIP_IPV6 */ + +#define ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \ + (netif) = ipX_route(isipv6, src, dest); \ + (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h new file mode 100644 index 0000000..7bd03cb --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV6 +/* A union struct for both IP version's addresses. */ +typedef union { + ip_addr_t ip4; + ip6_addr_t ip6; +} ipX_addr_t; + +/** These functions only exist for type-safe conversion from ip_addr_t to + ip6_addr_t and back */ +#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER +static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr) +{ return (ip6_addr_t*)ipaddr;} +static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr) +{ return (ip_addr_t*)ip6addr; } +static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr) +{ return (ipX_addr_t*)ipaddr; } +static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr) +{ return (ipX_addr_t*)ip6addr; } +#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */ +#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr)) +#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr)) +#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr) +#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr) +#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/ +#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6)) +#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4)) + +#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_copy((dest).ip6, (src).ip6); }else{ \ + ip_addr_copy((dest).ip4, (src).ip4); }}while(0) +#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), src); }}while(0) +#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \ + ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \ + ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \ + ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2))) +#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_isany(ipX_2_ip6(ipaddr)) : \ + ip_addr_isany(ipX_2_ip(ipaddr))) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \ + ip_addr_ismulticast(ipX_2_ip(ipaddr))) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \ + ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \ + ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0) + +#else /* LWIP_IPV6 */ + +typedef ip_addr_t ipX_addr_t; +#define ipX_2_ip(ipaddr) (ipaddr) +#define ip_2_ipX(ipaddr) (ipaddr) + +#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src) +#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr) +#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr) +#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2) +#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/mem.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/mem.h new file mode 100644 index 0000000..5bb906b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/mem.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +/* Since there is no C library allocation function to shrink memory without + moving it, define this to nothing. */ +#ifndef mem_trim +#define mem_trim(mem, size) (mem) +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000L +typedef u32_t mem_size_t; +#define MEM_SIZE_F U32_F +#else +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_trim is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_trim(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_trim(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ + +/** Calculate memory size for an aligned buffer - returns the next highest + * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and + * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). + */ +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +/** Calculate safe memory size for an aligned buffer when using an unaligned + * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the + * start (e.g. if buffer is u8_t[] and actual data will be u32_t*) + */ +#ifndef LWIP_MEM_ALIGN_BUFFER +#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1)) +#endif + +/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT + * so that ADDR % MEM_ALIGNMENT == 0 + */ +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEM_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/memp.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/memp.h new file mode 100644 index 0000000..f0d0739 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/memp.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_MEMP_H__ +#define __LWIP_MEMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/memp_std.h" + MEMP_MAX +} memp_t; + +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC || MEM_USE_POOLS +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC + +#include "mem.h" + +#define memp_init() +#define memp_malloc(type) mem_malloc(memp_sizes[type]) +#define memp_free(type, mem) mem_free(mem) + +#else /* MEMP_MEM_MALLOC */ + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. */ +struct memp_malloc_helper +{ + memp_t poolnr; +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h new file mode 100644 index 0000000..592a282 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h @@ -0,0 +1,135 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ +#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") +#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if !LWIP_TCPIP_CORE_LOCKING_INPUT +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ +#endif /* NO_SYS==0 */ + +#if LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */ +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* LWIP_TIMERS */ + +#if LWIP_SNMP +LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE") +LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE") +LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND") +LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE") +#endif /* LWIP_SNMP */ +#if LWIP_DNS && LWIP_SOCKET +LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB") +#endif /* LWIP_DNS && LWIP_SOCKET */ +#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST") +#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#if PPP_SUPPORT && PPPOE_SUPPORT +LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ + +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h new file mode 100644 index 0000000..d12fe27 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This netbuf has dest-addr/port set */ +#define NETBUF_FLAG_DESTADDR 0x01 +/** This netbuf includes a checksum */ +#define NETBUF_FLAG_CHKSUM 0x02 + +struct netbuf { + struct pbuf *p, *ptr; + ipX_addr_t addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + u8_t flags; +#endif /* LWIP_CHECKSUM_ON_COPY */ + u16_t toport_chksum; +#if LWIP_NETBUF_RECVINFO + ipX_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) (ipX_2_ip(&((buf)->addr))) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (ipX_2_ip(&((buf)->toaddr))) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), destaddr) +#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) +#endif /* LWIP_NETBUF_RECVINFO */ +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \ + (buf)->toport_chksum = chksum; } while(0) +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#if LWIP_IPV6 +#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr))) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr) +#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr))) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr) +#endif /* LWIP_IPV6 */ + +#define netbuf_fromaddr_ipX(buf) (&((buf)->addr)) +#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr)) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/netdb.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/netdb.h new file mode 100644 index 0000000..7587e2f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/netdb.h @@ -0,0 +1,124 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIP_NETDB_H__ +#define __LWIP_NETDB_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include /* for size_t */ + +#include "lwip/inet.h" +#include "lwip/sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessable error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +#define gethostbyname(name) lwip_gethostbyname(name) +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo) +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS && LWIP_SOCKET */ + +#endif /* __LWIP_NETDB_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/netif.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/netif.h new file mode 100644 index 0000000..f977032 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/netif.h @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETIF_H__ +#define __LWIP_NETIF_H__ + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** must be the maximum of all used hardware address lengths + across all types of interfaces in use */ +#define NETIF_MAX_HWADDR_LEN 6U + +/** Whether the network interface is 'up'. This is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + * It is set by the startup code (for static IP configuration) or + * by dhcp/autoip when an address has been assigned. + */ +#define NETIF_FLAG_UP 0x01U +/** If set, the netif has broadcast capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_BROADCAST 0x02U +/** If set, the netif is one end of a point-to-point connection. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** If set, the interface is configured using DHCP. + * Set by the DHCP code when starting or stopping DHCP. */ +#define NETIF_FLAG_DHCP 0x08U +/** If set, the interface has an active link + * (set by the network interface driver). + * Either set by the netif driver in its init function (if the link + * is up at that time) or at a later point once the link comes up + * (if link detection is supported by the hardware). */ +#define NETIF_FLAG_LINK_UP 0x10U +/** If set, the netif is an ethernet device using ARP. + * Set by the netif driver in its init function. + * Used to check input packet types and use of DHCP. */ +#define NETIF_FLAG_ETHARP 0x20U +/** If set, the netif is an ethernet device. It might not use + * ARP or TCP/IP if it is used for PPPoE only. + */ +#define NETIF_FLAG_ETHERNET 0x40U +/** If set, the netif has IGMP capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_IGMP 0x80U +/** Whether to pretend that we are every host for TCP packets. + * Set by netif_set_pretend_tcp. */ +#define NETIF_FLAG_PRETEND_TCP 0x100U + +/** Function prototype for netif init functions. Set up flags and output/linkoutput + * callback functions in this function. + * + * @param netif The netif to initialize + */ +typedef err_t (*netif_init_fn)(struct netif *netif); +/** Function prototype for netif->input functions. This function is saved as 'input' + * callback function in the netif struct. Call it when a packet has been received. + * + * @param p The received packet, copied into a pbuf + * @param inp The netif which received the packet + */ +typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); +/** Function prototype for netif->output functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'etharp_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IP address to which the packet shall be sent + */ +typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr); +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'nd_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ +/** Function prototype for netif->linkoutput functions. Only used for ethernet + * netifs. This function is called by ARP when a packet shall be sent. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (raw ethernet packet) + */ +typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p); +/** Function prototype for netif status- or link-callback functions. */ +typedef void (*netif_status_callback_fn)(struct netif *netif); +/** Function prototype for netif igmp_mac_filter functions */ +typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, + ip_addr_t *group, u8_t action); +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + ip6_addr_t *group, u8_t action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ +struct netif { + /** pointer to next in linked list */ + struct netif *next; + + /** IP address configuration in network byte order */ + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; + +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + netif_input_fn input; + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_fn output; + /** This function is called by the ARP module when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. */ + netif_linkoutput_fn linkoutput; +#if LWIP_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + netif_status_callback_fn status_callback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + netif_status_callback_fn link_callback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. */ + void *state; +#if LWIP_DHCP + /** the DHCP client state information for this netif */ + struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; +#endif +#if LWIP_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /** the DHCPv6 client state information for this netif */ + struct dhcp6 *dhcp6; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** maximum transfer unit (in bytes) */ + u16_t mtu; + /** number of bytes used in hwaddr */ + u8_t hwaddr_len; + /** link level hardware address of this interface */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** flags (see NETIF_FLAG_ above) */ + u16_t flags; + /** descriptive abbreviation */ + char name[2]; + /** number of this interface */ + u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /** This function could be called to add or delete an entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + (netif)->link_type = (type); \ + /* your link speed here (units: bits per second) */ \ + (netif)->link_speed = (speed); \ + (netif)->ts = 0; \ + (netif)->ifinoctets = 0; \ + (netif)->ifinucastpkts = 0; \ + (netif)->ifinnucastpkts = 0; \ + (netif)->ifindiscards = 0; \ + (netif)->ifoutoctets = 0; \ + (netif)->ifoutucastpkts = 0; \ + (netif)->ifoutnucastpkts = 0; \ + (netif)->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +void netif_init(void); + +struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input); + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw); +void netif_remove(struct netif * netif); + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(char *name); + +int netif_is_named (struct netif *netif, const char name[3]); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask); +void netif_set_gw(struct netif *netif, ip_addr_t *gw); +void netif_set_pretend_tcp(struct netif *netif, u8_t pretend); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** Ask if an interface is up */ +#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_STATUS_CALLBACK +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** Ask if a link is up */ +#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_HOSTNAME +#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0) +#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL) +#endif /* LWIP_NETIF_HOSTNAME */ + +#if LWIP_IGMP +#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0) +#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL) +#endif /* LWIP_IGMP */ + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)])) +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)]) +#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state)) +s8_t netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr); +void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit); +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETIF_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h new file mode 100644 index 0000000..33318ef --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h @@ -0,0 +1,108 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*netifapi_void_fn)(struct netif *netif); +typedef err_t (*netifapi_errt_fn)(struct netif *netif); + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + ip_addr_t *ipaddr; + ip_addr_t *netmask; + ip_addr_t *gw; + void *state; + netif_init_fn init; + netif_input_fn input; + } add; + struct { + netifapi_void_fn voidfunc; + netifapi_errt_fn errtfunc; + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input); + +err_t netifapi_netif_set_addr ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/opt.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/opt.h new file mode 100644 index 0000000..e51f8e5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/opt.h @@ -0,0 +1,2417 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_OPT_H__ +#define __LWIP_OPT_H__ + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 0 +#endif + +/** + * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1 + * Mainly for compatibility to old versions. + */ +#ifndef NO_SYS_NO_TIMERS +#define NO_SYS_NO_TIMERS 0 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** +* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. +* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution +* speed and usage from interrupts! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 1 +#endif + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array. + * This can be used to individually change the location of each pool. + * Default is one big array for all pools + */ +#ifndef MEMP_SEPARATE_POOLS +#define MEMP_SEPARATE_POOLS 0 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with IP_FRAG_USES_STATIC_BUF==0 and + * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs + * where the packet is not yet sent when netif->output returns. + */ +#ifndef MEMP_NUM_FRAG_PBUF +#define MEMP_NUM_FRAG_PBUF 15 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree. + */ +#ifndef MEMP_NUM_SNMP_NODE +#define MEMP_NUM_SNMP_NODE 50 +#endif + +/** + * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree. + * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least! + */ +#ifndef MEMP_NUM_SNMP_ROOTNODE +#define MEMP_NUM_SNMP_ROOTNODE 30 +#endif + +/** + * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to + * be changed normally) - 2 of these are used per request (1 for input, + * 1 for output) + */ +#ifndef MEMP_NUM_SNMP_VARBIND +#define MEMP_NUM_SNMP_VARBIND 2 +#endif + +/** + * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used + * (does not have to be changed normally) - 3 of these are used per request + * (1 for the value read and 2 for OIDs - input and output) + */ +#ifndef MEMP_NUM_SNMP_VALUE +#define MEMP_NUM_SNMP_VALUE 3 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#ifndef MEMP_NUM_NETDB +#define MEMP_NUM_NETDB 1 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#ifndef MEMP_NUM_LOCALHOSTLIST +#define MEMP_NUM_LOCALHOSTLIST 1 +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 0 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + * The peer *is* in the ARP table if it requested our address before. + * Also notice that this slows down input processing of every IP packet! + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 0 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP + * might be disabled + */ +#ifndef LWIP_ETHERNET +#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT) +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#ifndef ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1, + * new PBUF_RAM pbufs are used for fragments). + * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs! + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 0 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 0 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + * Does not have to be changed unless external MIBs answer request asynchronously + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + * When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 +#endif + +/** + * The maximum length of strings used. This affects the size of + * MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_OCTET_STRING_LEN +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum depth of the SNMP tree. + * With private MIBs enabled, this depends on your MIB! + * This affects the size of MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_TREE_DEPTH +#define SNMP_MAX_TREE_DEPTH 15 +#endif + +/** + * The size of the MEMP_SNMP_VALUE elements, normally calculated from + * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH. + */ +#ifndef SNMP_MAX_VALUE_SIZE +#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH)) +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#ifndef TCP_WND +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 536 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF (2 * TCP_MSS) +#endif + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#ifndef TCP_SNDQUEUELOWAT +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppInputThread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppInputThread" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppInputThread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#ifndef SO_REUSE_RXTOALL +#define SO_REUSE_RXTOALL 0 +#endif + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#ifndef LWIP_FIONREAD_LINUXMODE +#define LWIP_FIONREAD_LINUXMODE 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) +#endif + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#ifndef IP6_STATS +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#ifndef ICMP6_STATS +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#ifndef IP6_FRAG_STATS +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#ifndef MLD6_STATS +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#ifndef ND6_STATS +#define ND6_STATS (LWIP_IPV6) +#endif + +#else + +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 /* max length of password or secret */ +#endif + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#ifndef LWIP_CHECKSUM_ON_COPY +#define LWIP_CHECKSUM_ON_COPY 0 +#endif + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#ifndef LWIP_IPV6_NUM_ADDRESSES +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#ifndef LWIP_IPV6_FORWARD +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#ifndef LWIP_ICMP6 +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#ifndef LWIP_ICMP6_HL +#define LWIP_ICMP6_HL 255 +#endif + +/** + * LWIP_ICMP6_CHECKSUM_CHECK==1: verify checksum on ICMPv6 packets + */ +#ifndef LWIP_ICMP6_CHECKSUM_CHECK +#define LWIP_ICMP6_CHECKSUM_CHECK 1 +#endif + +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + */ +#ifndef LWIP_IPV6_MLD +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined. + */ +#ifndef MEMP_NUM_MLD6_GROUP +#define MEMP_NUM_MLD6_GROUP 4 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#ifndef LWIP_IPV6_FRAG +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#ifndef LWIP_IPV6_REASS +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#ifndef LWIP_ND6_QUEUEING +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#ifndef MEMP_NUM_ND6_QUEUE +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#ifndef LWIP_ND6_NUM_NEIGHBORS +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#ifndef LWIP_ND6_NUM_DESTINATIONS +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#ifndef LWIP_ND6_NUM_PREFIXES +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#ifndef LWIP_ND6_NUM_ROUTERS +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#ifndef LWIP_ND6_REACHABLE_TIME +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#ifndef LWIP_ND6_RETRANS_TIMER +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#ifndef LWIP_ND6_ALLOW_RA_UPDATES +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#ifndef LWIP_IPV6_AUTOCONFIG +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts. + */ +#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#ifndef LWIP_IPV6_DHCP6 +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#ifndef TIMERS_DEBUG +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_OFF +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h new file mode 100644 index 0000000..4f8dca8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_PBUF_H__ +#define __LWIP_PBUF_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the pbuf_custom code is only needed for one specific configuration + * of IP_FRAG */ +#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) + +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + +#define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else +#define PBUF_IP_HLEN 20 +#endif + +typedef enum { + PBUF_TRANSPORT, + PBUF_IP, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL /* pbuf payload refers to RAM */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U +/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a + a pbuf differently */ +#define PBUF_FLAG_IS_CUSTOM 0x02U +/** indicates this pbuf is UDP multicast to be looped back */ +#define PBUF_FLAG_MCASTLOOP 0x04U +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U + +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; +}; + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Prototype for a function to free a custom pbuf */ +typedef void (*pbuf_free_custom_fn)(struct pbuf *p); + +/** A custom pbuf: like a pbuf, but following a function pointer to free it. */ +struct pbuf_custom { + /** The actual pbuf */ + struct pbuf pbuf; + /** This function is called when pbuf_free deallocates this pbuf(_custom) */ + pbuf_free_custom_fn custom_free_function; +}; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ +#if NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(void); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type); +#if LWIP_SUPPORT_CUSTOM_PBUF +struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, + struct pbuf_custom *p, void *payload_mem, + u16_t payload_mem_len); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#if LWIP_CHECKSUM_ON_COPY +err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum); +#endif /* LWIP_CHECKSUM_ON_COPY */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset); +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/raw.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/raw.h new file mode 100644 index 0000000..f0c8ed4 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/raw.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_RAW_H__ +#define __LWIP_RAW_H__ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb; + +/** Function prototype for raw pcb receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr); + +#if LWIP_IPV6 +/** Function prototype for raw pcb IPv6 receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6; +#else +#define RAW_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + union { + raw_recv_fn ip4; + RAW_PCB_RECV_IP6 + } recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +#if LWIP_IPV6 +struct raw_pcb * raw_new_ip6 (u8_t proto); +#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr)) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr)) +#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg) +#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr)) +#endif /* LWIP_IPV6 */ + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/sio.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/sio.h new file mode 100644 index 0000000..28ae2f2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/sio.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * This is the interface to the platform specific serial IO module + * It needs to be implemented by those platforms which need SLIP or PPP + */ + +#ifndef __SIO_H__ +#define __SIO_H__ + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ +#ifndef __sio_fd_t_defined +typedef void * sio_fd_t; +#endif + +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + +#ifndef sio_open +/** + * Opens a serial device for communication. + * + * @param devnum device number + * @return handle to serial device if successful, NULL otherwise + */ +sio_fd_t sio_open(u8_t devnum); +#endif + +#ifndef sio_send +/** + * Sends a single character to the serial device. + * + * @param c character to send + * @param fd serial device handle + * + * @note This function will block until the character can be sent. + */ +void sio_send(u8_t c, sio_fd_t fd); +#endif + +#ifndef sio_recv +/** + * Receives a single character from the serial device. + * + * @param fd serial device handle + * + * @note This function will block until a character is received. + */ +u8_t sio_recv(sio_fd_t fd); +#endif + +#ifndef sio_read +/** + * Reads from the serial device. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received - may be 0 if aborted by sio_read_abort + * + * @note This function will block until data can be received. The blocking + * can be cancelled by calling sio_read_abort(). + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_tryread +/** + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_write +/** + * Writes to the serial device. + * + * @param fd serial device handle + * @param data pointer to data to send + * @param len length (in bytes) of data to send + * @return number of bytes actually sent + * + * @note This function will block until all data can be sent. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h new file mode 100644 index 0000000..2ed043d --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef __LWIP_SNMP_H__ +#define __LWIP_SNMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/ip_addr.h" + +struct udp_pcb; +struct netif; + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); + +/* network interface */ +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip); +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip); + +/* IP */ +void snmp_inc_ipinreceives(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); +void snmp_inc_ipindiscards(void); +void snmp_inc_ipindelivers(void); +void snmp_inc_ipoutrequests(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); + +/* ICMP */ +void snmp_inc_icmpinmsgs(void); +void snmp_inc_icmpinerrors(void); +void snmp_inc_icmpindestunreachs(void); +void snmp_inc_icmpintimeexcds(void); +void snmp_inc_icmpinparmprobs(void); +void snmp_inc_icmpinsrcquenchs(void); +void snmp_inc_icmpinredirects(void); +void snmp_inc_icmpinechos(void); +void snmp_inc_icmpinechoreps(void); +void snmp_inc_icmpintimestamps(void); +void snmp_inc_icmpintimestampreps(void); +void snmp_inc_icmpinaddrmasks(void); +void snmp_inc_icmpinaddrmaskreps(void); +void snmp_inc_icmpoutmsgs(void); +void snmp_inc_icmpouterrors(void); +void snmp_inc_icmpoutdestunreachs(void); +void snmp_inc_icmpouttimeexcds(void); +void snmp_inc_icmpoutparmprobs(void); +void snmp_inc_icmpoutsrcquenchs(void); +void snmp_inc_icmpoutredirects(void); +void snmp_inc_icmpoutechos(void); +void snmp_inc_icmpoutechoreps(void); +void snmp_inc_icmpouttimestamps(void); +void snmp_inc_icmpouttimestampreps(void); +void snmp_inc_icmpoutaddrmasks(void); +void snmp_inc_icmpoutaddrmaskreps(void); + +/* TCP */ +void snmp_inc_tcpactiveopens(void); +void snmp_inc_tcppassiveopens(void); +void snmp_inc_tcpattemptfails(void); +void snmp_inc_tcpestabresets(void); +void snmp_inc_tcpinsegs(void); +void snmp_inc_tcpoutsegs(void); +void snmp_inc_tcpretranssegs(void); +void snmp_inc_tcpinerrs(void); +void snmp_inc_tcpoutrsts(void); + +/* UDP */ +void snmp_inc_udpindatagrams(void); +void snmp_inc_udpnoports(void); +void snmp_inc_udpinerrors(void); +void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); + +/* LWIP_SNMP support not available */ +/* define everything to be empty */ +#else + +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + +/* network interface */ +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) + +/* IP */ +#define snmp_inc_ipinreceives() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() +#define snmp_inc_ipindiscards() +#define snmp_inc_ipindelivers() +#define snmp_inc_ipoutrequests() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) + +/* ICMP */ +#define snmp_inc_icmpinmsgs() +#define snmp_inc_icmpinerrors() +#define snmp_inc_icmpindestunreachs() +#define snmp_inc_icmpintimeexcds() +#define snmp_inc_icmpinparmprobs() +#define snmp_inc_icmpinsrcquenchs() +#define snmp_inc_icmpinredirects() +#define snmp_inc_icmpinechos() +#define snmp_inc_icmpinechoreps() +#define snmp_inc_icmpintimestamps() +#define snmp_inc_icmpintimestampreps() +#define snmp_inc_icmpinaddrmasks() +#define snmp_inc_icmpinaddrmaskreps() +#define snmp_inc_icmpoutmsgs() +#define snmp_inc_icmpouterrors() +#define snmp_inc_icmpoutdestunreachs() +#define snmp_inc_icmpouttimeexcds() +#define snmp_inc_icmpoutparmprobs() +#define snmp_inc_icmpoutsrcquenchs() +#define snmp_inc_icmpoutredirects() +#define snmp_inc_icmpoutechos() +#define snmp_inc_icmpoutechoreps() +#define snmp_inc_icmpouttimestamps() +#define snmp_inc_icmpouttimestampreps() +#define snmp_inc_icmpoutaddrmasks() +#define snmp_inc_icmpoutaddrmaskreps() +/* TCP */ +#define snmp_inc_tcpactiveopens() +#define snmp_inc_tcppassiveopens() +#define snmp_inc_tcpattemptfails() +#define snmp_inc_tcpestabresets() +#define snmp_inc_tcpinsegs() +#define snmp_inc_tcpoutsegs() +#define snmp_inc_tcpretranssegs() +#define snmp_inc_tcpinerrs() +#define snmp_inc_tcpoutrsts() + +/* UDP */ +#define snmp_inc_udpindatagrams() +#define snmp_inc_udpnoports() +#define snmp_inc_udpinerrors() +#define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) + +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_asn1.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_asn1.h new file mode 100644 index 0000000..605fa3f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_asn1.h @@ -0,0 +1,101 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_ASN1_H__ +#define __LWIP_SNMP_ASN1_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" + +#if LWIP_SNMP + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */ +#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */ +#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */ + +#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */ +#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */ + +/* universal tags */ +#define SNMP_ASN1_INTEG 2 +#define SNMP_ASN1_OC_STR 4 +#define SNMP_ASN1_NUL 5 +#define SNMP_ASN1_OBJ_ID 6 +#define SNMP_ASN1_SEQ 16 + +/* application specific (SNMP) tags */ +#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */ +#define SNMP_ASN1_COUNTER 1 /* u32_t */ +#define SNMP_ASN1_GAUGE 2 /* u32_t */ +#define SNMP_ASN1_TIMETICKS 3 /* u32_t */ +#define SNMP_ASN1_OPAQUE 4 /* octet string */ + +/* context specific (SNMP) tags */ +#define SNMP_ASN1_PDU_GET_REQ 0 +#define SNMP_ASN1_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_PDU_GET_RESP 2 +#define SNMP_ASN1_PDU_SET_REQ 3 +#define SNMP_ASN1_PDU_TRAP 4 + +err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type); +err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length); +err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid); +err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed); +err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type); +err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length); +err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value); +err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident); +err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h new file mode 100644 index 0000000..1183e3a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h @@ -0,0 +1,315 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + ip_addr_t sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + ip_addr_t dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h new file mode 100644 index 0000000..0d3b46a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h @@ -0,0 +1,268 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB access types */ +#define MIB_ACCESS_READ 1 +#define MIB_ACCESS_WRITE 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ +#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE) +#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE +#define MIB_OBJECT_NOT_ACCESSIBLE 0 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + u8_t node_type; + /* array or max list length */ + u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + /** points to an external (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, ip_addr_t *ip); +void snmp_iptooid(ip_addr_t *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h new file mode 100644 index 0000000..7346137 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef __LWIP_SOCKETS_H__ +#define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "lwip/inet6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; + +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + u8_t sin6_family; /* AF_INET6 */ + u16_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ +}; +#endif /* LWIP_IPV6 */ + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; +#if LWIP_IPV6 + u8_t sa_data[22]; +#else /* LWIP_IPV6 */ + u8_t sa_data[14]; +#endif /* LWIP_IPV6 */ +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ + +#define SO_DONTLINGER ((int)(~SO_LINGER)) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#endif /* LWIP_IPV6 */ +#define IPPROTO_UDPLITE 136 + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET + #undef FD_SETSIZE + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + unsigned char fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ + +void lwip_socket_init(void); + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, size_t size); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); +int lwip_fcntl(int s, int cmd, int val); + +#if LWIP_COMPAT_SOCKETS +#define accept(a,b,c) lwip_accept(a,b,c) +#define bind(a,b,c) lwip_bind(a,b,c) +#define shutdown(a,b) lwip_shutdown(a,b) +#define closesocket(s) lwip_close(s) +#define connect(a,b,c) lwip_connect(a,b,c) +#define getsockname(a,b,c) lwip_getsockname(a,b,c) +#define getpeername(a,b,c) lwip_getpeername(a,b,c) +#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e) +#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) +#define listen(a,b) lwip_listen(a,b) +#define recv(a,b,c,d) lwip_recv(a,b,c,d) +#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) +#define send(a,b,c,d) lwip_send(a,b,c,d) +#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) +#define socket(a,b,c) lwip_socket(a,b,c) +#define select(a,b,c,d,e) lwip_select(a,b,c,d,e) +#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/stats.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/stats.h new file mode 100644 index 0000000..d911216 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/stats.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_STATS_H__ +#define __LWIP_STATS_H__ + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER rx_v1; /* Received v1 frames. */ + STAT_COUNTER rx_group; /* Received group-specific queries. */ + STAT_COUNTER rx_general; /* Received general queries. */ + STAT_COUNTER rx_report; /* Received reports. */ + STAT_COUNTER tx_join; /* Sent joins. */ + STAT_COUNTER tx_leave; /* Sent leaves. */ + STAT_COUNTER tx_report; /* Sent reports. */ +}; + +struct stats_mem { +#ifdef LWIP_DEBUG + const char *name; +#endif /* LWIP_DEBUG */ + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; +}; + +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mutex; + struct stats_syselem mbox; +}; + +struct stats_ { +#if LINK_STATS + struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + struct stats_proto ip_frag; +#endif +#if IP_STATS + struct stats_proto ip; +#endif +#if ICMP_STATS + struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS + struct stats_proto udp; +#endif +#if TCP_STATS + struct stats_proto tcp; +#endif +#if MEM_STATS + struct stats_mem mem; +#endif +#if MEMP_STATS + struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS + struct stats_sys sys; +#endif +#if IP6_STATS + struct stats_proto ip6; +#endif +#if ICMP6_STATS + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + struct stats_igmp mld6; +#endif +#if ND6_STATS + struct stats_proto nd6; +#endif +}; + +extern struct stats_ lwip_stats; + +void stats_init(void); + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \ + if (lwip_stats.x.max < lwip_stats.x.used) { \ + lwip_stats.x.max = lwip_stats.x.used; \ + } \ + } while(0) +#else /* LWIP_STATS */ +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#define STATS_INC_USED(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP") +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_INC_USED(x) +#define SYS_STATS_DISPLAY() +#endif + +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp, name) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_STATS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/sys.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/sys.h new file mode 100644 index 0000000..fd45ee8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/sys.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_SYS_H__ +#define __LWIP_SYS_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mutex_t; +typedef u8_t sys_mbox_t; + +#define sys_sem_new(s, c) ERR_OK +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_sem_valid(s) 0 +#define sys_sem_set_invalid(s) +#define sys_mutex_new(mu) ERR_OK +#define sys_mutex_lock(mu) +#define sys_mutex_unlock(mu) +#define sys_mutex_free(mu) +#define sys_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(mu) +#define sys_mbox_new(m, s) ERR_OK +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) +#define sys_mbox_valid(m) +#define sys_mbox_set_invalid(m) + +#define sys_thread_new(n,t,a,s,p) + +#define sys_msleep(t) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +/** Function prototype for thread functions */ +typedef void (*lwip_thread_fn)(void *arg); + +/* Function prototypes for functions to be implemented by platform ports + (in sys_arch.c) */ + +/* Mutex functions: */ + +/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#if LWIP_COMPAT_MUTEX +/* for old ports that don't have mutexes: define them to binary semaphores */ +#define sys_mutex_t sys_sem_t +#define sys_mutex_new(mutex) sys_sem_new(mutex, 1) +#define sys_mutex_lock(mutex) sys_sem_wait(mutex) +#define sys_mutex_unlock(mutex) sys_sem_signal(mutex) +#define sys_mutex_free(mutex) sys_sem_free(mutex) +#define sys_mutex_valid(mutex) sys_sem_valid(mutex) +#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex) + +#else /* LWIP_COMPAT_MUTEX */ + +/** Create a new mutex + * @param mutex pointer to the mutex to create + * @return a new mutex */ +err_t sys_mutex_new(sys_mutex_t *mutex); +/** Lock a mutex + * @param mutex the mutex to lock */ +void sys_mutex_lock(sys_mutex_t *mutex); +/** Unlock a mutex + * @param mutex the mutex to unlock */ +void sys_mutex_unlock(sys_mutex_t *mutex); +/** Delete a semaphore + * @param mutex the mutex to delete */ +void sys_mutex_free(sys_mutex_t *mutex); +#ifndef sys_mutex_valid +/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mutex_valid(sys_mutex_t *mutex); +#endif +#ifndef sys_mutex_set_invalid +/** Set a mutex invalid so that sys_mutex_valid returns 0 */ +void sys_mutex_set_invalid(sys_mutex_t *mutex); +#endif +#endif /* LWIP_COMPAT_MUTEX */ + +/* Semaphore functions: */ + +/** Create a new semaphore + * @param sem pointer to the semaphore to create + * @param count initial count of the semaphore + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count); +/** Signals a semaphore + * @param sem the semaphore to signal */ +void sys_sem_signal(sys_sem_t *sem); +/** Wait for a semaphore for the specified timeout + * @param sem the semaphore to wait for + * @param timeout timeout in milliseconds to wait (0 = wait forever) + * @return time (in milliseconds) waited for the semaphore + * or SYS_ARCH_TIMEOUT on timeout */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout); +/** Delete a semaphore + * @param sem semaphore to delete */ +void sys_sem_free(sys_sem_t *sem); +/** Wait for a semaphore - forever/no timeout */ +#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0) +#ifndef sys_sem_valid +/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_sem_valid(sys_sem_t *sem); +#endif +#ifndef sys_sem_set_invalid +/** Set a semaphore invalid so that sys_sem_valid returns 0 */ +void sys_sem_set_invalid(sys_sem_t *sem); +#endif + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif + +/* Mailbox functions. */ + +/** Create a new mbox of specified size + * @param mbox pointer to the mbox to create + * @param size (miminum) number of messages in this mbox + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size); +/** Post a message to an mbox - may not fail + * -> blocks if full, only used from tasks not from ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg); +/** Try to post a message to an mbox - may fail if full or ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg); +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever) + * @return time (in milliseconds) waited for a message, may be 0 if not waited + or SYS_ARCH_TIMEOUT on timeout + * The returned time has to be accurate to prevent timer jitter! */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout); +/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */ +#ifndef sys_arch_mbox_tryfetch +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @return 0 (milliseconds) if a message has been received + * or SYS_MBOX_EMPTY if the mailbox is empty */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg); +#endif +/** For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +/** Delete an mbox + * @param mbox mbox to delete */ +void sys_mbox_free(sys_mbox_t *mbox); +#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0) +#ifndef sys_mbox_valid +/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mbox_valid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_set_invalid +/** Set an mbox invalid so that sys_mbox_valid returns 0 */ +void sys_mbox_set_invalid(sys_mbox_t *mbox); +#endif + +/** The only thread function: + * Creates a new thread + * @param name human-readable name for the thread (used for debugging purposes) + * @param thread thread-function + * @param arg parameter passed to 'thread' + * @param stacksize stack size in bytes for the new thread (may be ignored by ports) + * @param prio priority of the new thread (may be ignored by ports) */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/* sys_init() must be called before anthing else. */ +void sys_init(void); + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void); +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +u32_t sys_now(void); + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SYS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h new file mode 100644 index 0000000..535b6a3 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_H__ +#define __LWIP_TCP_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/** Function prototype for tcp accept callback functions. Called when a new + * connection can be accepted on a listening pcb. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param newpcb The new connection pcb + * @param err An error code if there has been an error accepting. + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err); + +/** Function prototype for tcp receive callback functions. Called when data has + * been received. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which received data + * @param p The received data (or NULL when the connection has been closed!) + * @param err An error code if there has been an error receiving + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err); + +/** Function prototype for tcp sent callback functions. Called when sent data has + * been acknowledged by the remote side. Use it to free corresponding resources. + * This also means that the pcb has now space available to send new data. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb for which data has been acknowledged + * @param len The amount of bytes acknowledged + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb, + u16_t len); + +/** Function prototype for tcp poll callback functions. Called periodically as + * specified by @see tcp_poll. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb tcp pcb + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb); + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +typedef void (*tcp_err_fn)(void *arg, err_t err); + +/** Function prototype for tcp connected callback functions. Called when a pcb + * is connected to the remote side after initiating a connection attempt by + * calling tcp_connect(). + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which is connected + * @param err An unused error code, always ERR_OK currently ;-) TODO! + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + * + * @note When a connection attempt fails, the error callback is currently called! + */ +typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ +#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept; +#else /* LWIP_CALLBACK_API */ +#define DEF_ACCEPT_CALLBACK +#endif /* LWIP_CALLBACK_API */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* ports are in host byte order */ \ + int bound_to_netif; \ + u16_t local_port; \ + char local_netif[3] + + +/* the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + u8_t flags; +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + u16_t rcv_wnd; /* receiver window available */ + u16_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + u16_t snd_wnd; /* sender window */ + u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ + +#if TCP_OVERSIZE + /* Extra bytes available at the end of the last pbuf in unsent. */ + u16_t unsent_oversize; +#endif /* TCP_OVERSIZE */ + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. */ + tcp_sent_fn sent; + /* Function to be called when (in-sequence) data has arrived. */ + tcp_recv_fn recv; + /* Function to be called when a connection has been set up. */ + tcp_connected_fn connected; + /* Function which is called periodically. */ + tcp_poll_fn poll; + /* Function to be called whenever a fatal error occurs. */ + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u8_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; +}; + +struct tcp_pcb_listen { +/* Common members of all PCB types */ + IP_PCB; +/* Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +#if LWIP_IPV6 + u8_t accept_any_ip_version; +#endif /* LWIP_IPV6 */ +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#endif /* LWIP_EVENT_API */ + +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); + +#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY) +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) do { \ + LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \ + (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \ + (pcb)->state == LISTEN) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t tcp_bind_to_netif (struct tcp_pcb *pcb, const char ifname[3]); +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* Flags for "apiflags" parameter in tcp_write */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + +#if LWIP_IPV6 +struct tcp_pcb * tcp_new_ip6 (void); +#define tcp_bind_ip6(pcb, ip6addr, port) \ + tcp_bind(pcb, ip6_2_ip(ip6addr), port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected) +struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) +#else /* LWIP_IPV6 */ +#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog) +#define tcp_listen_dual(pcb) tcp_listen(pcb) +#endif /* LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h new file mode 100644 index 0000000..2afc20d --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_IMPL_H__ +#define __LWIP_TCP_IMPL_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U + +#define TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) +#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags)) + +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) + +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0)) + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_EVENT_API + +#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, NULL, 0, ERR_OK) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_POLL, NULL, 0, ERR_OK) +#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ + LWIP_EVENT_ERR, NULL, 0, (err)) + +#else /* LWIP_EVENT_API */ + +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + do { \ + if((pcb)->accept != NULL) \ + (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_ARG; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CLOSED(pcb,ret) \ + do { \ + if(((pcb)->recv != NULL)) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\ + } else { \ + (ret) = ERR_OK; \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(errf,arg,err) \ + do { \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + +#endif /* LWIP_EVENT_API */ + +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ +#if TCP_OVERSIZE && defined(LWIP_DEBUG) +#define TCP_OVERSIZE_DBGCHECK 1 +#else +#define TCP_OVERSIZE_DBGCHECK 0 +#endif + +/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + +/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segements on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + u16_t len; /* the TCP length of this segment */ +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_left; /* Extra bytes available at the end of the last + pbuf in unsent (used for asserting vs. + tcp_pcb.unsent_oversized only) */ +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \ + (flags & TF_SEG_OPTS_TS ? 12 : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern struct tcp_pcb *tcp_bound_pcbs; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifndef TCP_DEBUG_PCB_LISTS +#define TCP_DEBUG_PCB_LISTS 0 +#endif +#if TCP_DEBUG_PCB_LISTS +#define TCP_REG(pcbs, npcb) do {\ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \ + for(tcp_tmp_pcb = *(pcbs); \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWIP_DEBUG */ + +#define TCP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *pcbs; \ + *(pcbs) = (npcb); \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWIP_DEBUG */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port) +#endif /* LWIP_IPV6 */ + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 */ +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +/** External function (implemented in timers.c), called when TCP detects + * that a timer is needed (i.e. active- or time-wait-pcb found). */ +void tcp_timer_needed(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h new file mode 100644 index 0000000..04567f2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCPIP_H__ +#define __LWIP_TCPIP_H__ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" +#include "lwip/netifapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. */ +#ifndef LWIP_TCPIP_THREAD_ALIVE +#define LWIP_TCPIP_THREAD_ALIVE() +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_mutex_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) +#ifdef LWIP_DEBUG +#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */ +#else +#define TCIP_APIMSG_SET_ERR(m, e) +#endif +#define TCPIP_APIMSG_NOERR(m,f) do { \ + TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + LOCK_TCPIP_CORE(); \ + f(&((m)->msg)); \ + UNLOCK_TCPIP_CORE(); \ +} while(0) +#define TCPIP_APIMSG(m,f,e) do { \ + TCPIP_APIMSG_NOERR(m,f); \ + (e) = (m)->msg.err; \ +} while(0) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** Function prototype for the init_done function passed to tcpip_init */ +typedef void (*tcpip_init_done_fn)(void *arg); +/** Function prototype for functions passed to tcpip_callback() */ +typedef void (*tcpip_callback_fn)(void *ctx); + +/* Forward declarations */ +struct tcpip_callback_msg; + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#endif /* LWIP_NETCONN */ + +err_t tcpip_input(struct pbuf *p, struct netif *inp); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +#if LWIP_TCPIP_TIMEOUT +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); +#endif /* LWIP_TCPIP_TIMEOUT */ + +enum tcpip_msg_type { +#if LWIP_NETCONN + TCPIP_MSG_API, +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ +#if LWIP_TCPIP_TIMEOUT + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN + struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ + struct { + struct pbuf *p; + struct netif *netif; + } inp; + struct { + tcpip_callback_fn function; + void *ctx; + } cb; +#if LWIP_TCPIP_TIMEOUT + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; +#endif /* LWIP_TCPIP_TIMEOUT */ + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/timers.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/timers.h new file mode 100644 index 0000000..04e78e0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/timers.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef __LWIP_TIMERS_H__ +#define __LWIP_TIMERS_H__ + +#include "lwip/opt.h" + +/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */ +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) + +#if LWIP_TIMERS + +#include "lwip/err.h" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void); + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg); +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg); +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg); +#endif /* NO_SYS */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TIMERS */ +#endif /* __LWIP_TIMERS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/lwip/udp.h b/app/src/main/jni/badvpn/lwip/src/include/lwip/udp.h new file mode 100644 index 0000000..14d5c0a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/lwip/udp.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_UDP_H__ +#define __LWIP_UDP_H__ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U +#define UDP_FLAGS_MULTICAST_LOOP 0x08U + +struct udp_pcb; + +/** Function prototype for udp pcb receive callback functions + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf + * makes 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port); + +#if LWIP_IPV6 +/** Function prototype for udp pcb IPv6 receive callback functions + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr, u16_t port); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6; +#else +#define UDP_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct udp_pcb { +/* Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /** ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_IGMP + /** outgoing network interface for multicast packets */ + ip_addr_t multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /** receive callback function */ + union { + udp_recv_fn ip4; + UDP_PCB_RECV_IP6 + }recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, u8_t have_chksum, + u16_t chksum); +err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + u8_t have_chksum, u16_t chksum); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp); + +void udp_init (void); + +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, ip6_2_ip(ip6addr), port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, ip6_2_ip(ip6addr), port) +#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \ + udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg) +#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \ + udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif) +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \ + udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum) +#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \ + udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum) +#endif /*LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/netif/etharp.h b/app/src/main/jni/badvpn/lwip/src/include/netif/etharp.h new file mode 100644 index 0000000..8275a28 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/netif/etharp.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u8_t hwlen); + PACK_STRUCT_FIELD(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_IPV6 0x86DDU +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr); +err_t etharp_remove_static_entry(ip_addr_t *ipaddr); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#endif /* __NETIF_ARP_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h b/app/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h new file mode 100644 index 0000000..e1cdfa5 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h @@ -0,0 +1,190 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + +#ifdef PPPOE_TODO + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +/** used in ppp.c */ +#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN) + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/include/netif/slipif.h b/app/src/main/jni/badvpn/lwip/src/include/netif/slipif.h new file mode 100644 index 0000000..7b6ce5e --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/netif/slipif.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __NETIF_SLIPIF_H__ +#define __NETIF_SLIPIF_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" + +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serila line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/app/src/main/jni/badvpn/lwip/src/include/posix/netdb.h b/app/src/main/jni/badvpn/lwip/src/include/posix/netdb.h new file mode 100644 index 0000000..7134032 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/netdb.h" diff --git a/app/src/main/jni/badvpn/lwip/src/include/posix/sys/socket.h b/app/src/main/jni/badvpn/lwip/src/include/posix/sys/socket.h new file mode 100644 index 0000000..f7c7066 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/sockets.h" diff --git a/app/src/main/jni/badvpn/lwip/src/netif/FILES b/app/src/main/jni/badvpn/lwip/src/netif/FILES new file mode 100644 index 0000000..099dbf3 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/FILES @@ -0,0 +1,29 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The PPP stack has been ported from ucip (http://ucip.sourceforge.net). + It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although + compared to that, it has some modifications for embedded systems and + the source code has been reordered a bit. \ No newline at end of file diff --git a/app/src/main/jni/badvpn/lwip/src/netif/etharp.c b/app/src/main/jni/badvpn/lwip/src/netif/etharp.c new file mode 100644 index 0000000..1b7eb98 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/etharp.c @@ -0,0 +1,1413 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_gratuitous(our_netif) upon address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "lwip/ip_addr.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; + +/** The 24-bit IANA multicast OUI is 01-00-5e: */ +#define LL_MULTICAST_ADDR_0 0x01 +#define LL_MULTICAST_ADDR_1 0x00 +#define LL_MULTICAST_ADDR_2 0x5e + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) + +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** Pointer to queue of pending outgoing packets on this ARP entry. */ + struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; +#endif /* ARP_QUEUEING */ + ip_addr_t ipaddr; + struct netif *netif; + struct eth_addr ethaddr; + u8_t state; + u8_t ctime; +}; + +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /* !LWIP_NETIF_HWADDRHINT */ + +/** Try hard to create a new entry - we want the IP address to appear in + the cache (even if this means removing an active entry or so). */ +#define ETHARP_FLAG_TRY_HARD 1 +#define ETHARP_FLAG_FIND_ONLY 2 +#if ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_NETIF_HWADDRHINT +#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +#else /* LWIP_NETIF_HWADDRHINT */ +#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) +#endif /* LWIP_NETIF_HWADDRHINT */ + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + +#endif /* ARP_QUEUEING */ + +/** Clean up ARP table entries */ +static void +etharp_free_entry(int i) +{ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; + arp_table[i].netif = NULL; + ip_addr_set_zero(&arp_table[i].ipaddr); + arp_table[i].ethaddr = ethzero; +#endif /* LWIP_DEBUG */ +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if (state != ETHARP_STATE_EMPTY +#if ETHARP_SUPPORT_STATIC_ENTRIES + && (state != ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + ) { + arp_table[i].ctime++; + if ((arp_table[i].ctime >= ARP_MAXAGE) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + etharp_free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif /* ARP_QUEUEING */ + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags @see definition of ETHARP_FLAG_* + * @param netif netif related to this address (used for NETIF_HWADDRHINT) + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t +etharp_find_entry(ip_addr_t *ipaddr, u8_t flags) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } else if (state != ETHARP_STATE_EMPTY) { + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE); + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + } + /* pending entry? */ + if (state == ETHARP_STATE_PENDING) { + /* pending with queued packets? */ + if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } + } else + /* pending without queued packets? */ + { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + /* stable entry? */ + } else if (state >= ETHARP_STATE_STABLE) { +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* don't record old_stable for static entries since they never expire */ + if (state < ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + } + } + /* { we have no match } => try to create a new entry */ + + /* don't create new entry, only search? */ + if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || + /* or no empty entry found and not allowed to recycle? */ + ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_FLAG_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } else { + /* 2) found recyclable stable entry? */ + if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending (queued packets are free in etharp_free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + /* no empty or recyclable entries found */ + } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n")); + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + etharp_free_entry(i); + } + + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY", + arp_table[i].state == ETHARP_STATE_EMPTY); + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_copy(arp_table[i].ipaddr, *ipaddr); + } + arp_table[i].ctime = 0; + return (err_t)i; +} + +/** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags @see definition of ETHARP_FLAG_* + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t +etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, flags); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + +#if ETHARP_SUPPORT_STATIC_ENTRIES + if (flags & ETHARP_FLAG_STATIC_ENTRY) { + /* record static type */ + arp_table[i].state = ETHARP_STATE_STATIC; + } else +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + + /* record network interface */ + arp_table[i].netif = netif; + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); + /* reset time stamp */ + arp_table[i].ctime = 0; + /* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } + return ERR_OK; +} + +#if ETHARP_SUPPORT_STATIC_ENTRIES +/** Add a new static entry to the ARP table. If an entry exists for the + * specified IP address, this entry is overwritten. + * If packets are queued for the specified IP address, they are sent out. + * + * @param ipaddr IP address for the new static entry + * @param ethaddr ethernet address for the new static entry + * @return @see return values of etharp_add_static_entry + */ +err_t +etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) +{ + struct netif *netif; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + + netif = ip_route(ipaddr); + if (netif == NULL) { + return ERR_RTE; + } + + return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); +} + +/** Remove a static entry from the ARP table previously added with a call to + * etharp_add_static_entry. + * + * @param ipaddr IP address of the static entry to remove + * @return ERR_OK: entry removed + * ERR_MEM: entry wasn't found + * ERR_ARG: entry wasn't a static entry but a dynamic one + */ +err_t +etharp_remove_static_entry(ip_addr_t *ipaddr) +{ + s8_t i; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + + if (arp_table[i].state != ETHARP_STATE_STATIC) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + etharp_free_entry(i); + return ERR_OK; +} +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + etharp_free_entry(i); + } + } +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret) +{ + s8_t i; + + LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL", + eth_ret != NULL && ip_ret != NULL); + + LWIP_UNUSED_ARG(netif); + + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +#if ETHARP_TRUST_IP_MAC +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param p The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +static void +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + ip_addr_t iphdr_src; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + ip_addr_copy(iphdr_src, iphdr->src); + + /* source is not on the local network? */ + if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update the source IP address in the cache, if present */ + /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); +} +#endif /* ETHARP_TRUST_IP_MAC */ + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. + * + * @return NULL + * + * @see pbuf_free() + */ +static void +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + struct eth_hdr *ethhdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + ip_addr_t sipaddr, dipaddr; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < SIZEOF_ETHARP_PACKET) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, + (s16_t)SIZEOF_ETHARP_PACKET)); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { + hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || + (hdr->hwlen != ETHARP_HWADDR_LEN) || + (hdr->protolen != sizeof(ip_addr_t)) || + (hdr->proto != PP_HTONS(ETHTYPE_IP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + /* this interface is not configured? */ + if (ip_addr_isany(&netif->ip_addr)) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? + -> add IP address in ARP cache; assume requester wants to talk to us, + can result in directly sending the queued packets for this host. + ARP message not directed to us? + -> update the source IP address in the cache, if present */ + etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); + + /* now act on the message itself */ + switch (hdr->opcode) { + /* ARP request? */ + case PP_HTONS(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ + hdr->opcode = htons(ARP_REPLY); + + IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr); + IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr); +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(&hdr->shwaddr, ethaddr); + ETHADDR16_COPY(ðhdr->src, ethaddr); + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* we are not configured? */ + } else if (ip_addr_isany(&netif->ip_addr)) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case PP_HTONS(ARP_REPLY): + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) +{ + struct eth_addr *dest; + struct eth_addr mcastaddr; + ip_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = LL_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) && + !ip_addr_islinklocal(ipaddr)) { +#if LWIP_AUTOIP + struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload + + sizeof(struct eth_hdr)); + /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with + a link-local source address must always be "directly to its destination + on the same physical link. The host MUST NOT send the packet to any + router for forwarding". */ + if (!ip_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + dst_addr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + } +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t etharp_cached_entry = *(netif->addr_hint); + if (etharp_cached_entry < ARP_TABLE_SIZE) { +#endif /* LWIP_NETIF_HWADDRHINT */ + if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state >= ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + if (q == NULL) { + return result; + } + } + + /* packet given? */ + LWIP_ASSERT("q != NULL", q != NULL); + /* stable entry? */ + if (arp_table[i].state >= ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + ETHARP_SET_HINT(netif, i); + /* send the packet */ + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry is still pending, queue the given packet 'q' */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ +#if ARP_QUEUEING + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); + } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + struct eth_hdr *ethhdr; + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_PACKET)); + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write the ARP MAC-Addresses */ + ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); + ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, ethdst_addr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->src, ethsrc_addr); + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing. */ + IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); + IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); + + hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); + hdr->proto = PP_HTONS(ETHTYPE_IP); + /* set hwlen and protolen */ + hdr->hwlen = ETHARP_HWADDR_LEN; + hdr->protolen = sizeof(ip_addr_t); + + ethhdr->type = PP_HTONS(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, ip_addr_t *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} +#endif /* LWIP_ARP */ + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = (struct eth_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + type = ethhdr->type; +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef ETHARP_VLAN_CHECK_FN + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ + type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + } +#endif /* ETHARP_SUPPORT_VLAN */ + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type)); +#endif /* LWIP_ARP_FILTER_NETIF*/ + + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { + if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } + } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + switch (type) { +#if LWIP_ARP + /* IP packet? */ + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -ip_hdr_offset)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; +#endif /* LWIP_ARP */ +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IPv6 layer */ + ip6_input(p, netif); + } + break; +#endif /* LWIP_IPV6 */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ethernetif.c b/app/src/main/jni/badvpn/lwip/src/netif/ethernetif.c new file mode 100644 index 0000000..46900bd --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ethernetif.c @@ -0,0 +1,322 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_IPV6: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.c new file mode 100644 index 0000000..0fd87a3 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.c @@ -0,0 +1,1334 @@ +/***************************************************************************** +* auth.c - Network Authentication and Phase Control program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Ported from public pppd code. +*****************************************************************************/ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "lcp.h" +#include "pap.h" +#include "chap.h" +#include "auth.h" +#include "ipcp.h" + +#if CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + +#include "lwip/inet.h" + +#include + +#if 0 /* UNUSED */ +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) +#endif /* UNUSED */ + +#if PAP_SUPPORT || CHAP_SUPPORT +/* The name by which the peer authenticated itself to us. */ +static char peer_authname[MAXNAMELEN]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called plogin() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; /* @todo, we don't need this in lwip*/ + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +#if 0 /* UNUSED */ +/* Wordlist giving addresses which the peer may use + without authenticating itself. */ +static struct wordlist *noauth_addrs; + +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; +#endif /* UNUSED */ + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +#if PAP_SUPPORT || CHAP_SUPPORT +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* Set if we require authentication only because we have a default route. */ +static bool default_auth; + +/* Hook to enable a plugin to control the idle time limit */ +int (*idle_time_hook) __P((struct ppp_idle *)) = NULL; + +/* Hook for a plugin to say whether we can possibly authenticate any peer */ +int (*pap_check_hook) __P((void)) = NULL; + +/* Hook for a plugin to check the PAP user and password */ +int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, + struct wordlist **paddrs, + struct wordlist **popts)) = NULL; + +/* Hook for a plugin to know about the PAP user logout */ +void (*pap_logout_hook) __P((void)) = NULL; + +/* Hook for a plugin to get the PAP password for authenticating us */ +int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL; + +/* + * This is used to ensure that we don't start an auth-up/down + * script while one is already running. + */ +enum script_state { + s_down, + s_up +}; + +static enum script_state auth_state = s_down; +static enum script_state auth_script_state = s_down; +static pid_t auth_script_pid = 0; + +/* + * Option variables. + * lwip: some of these are present in the ppp_settings structure + */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ +bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +bool usehostname = 0; /* Use hostname for our_name */ +bool auth_required = 0; /* Always require authentication from peer */ +bool allow_any_ip = 0; /* Allow peer to use any IP address */ +bool explicit_remote = 0; /* User specified explicit remote name */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ + +#endif /* UNUSED */ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +/* @todo, move this somewhere */ +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + + +extern char *crypt (const char *, const char *); + +/* Prototypes for procedures local to this file. */ + +static void network_phase (int); +static void check_idle (void *); +static void connect_time_expired (void *); +#if 0 +static int plogin (char *, char *, char **, int *); +#endif +static void plogout (void); +static int null_login (int); +static int get_pap_passwd (int, char *, char *); +static int have_pap_secret (void); +static int have_chap_secret (char *, char *, u32_t); +static int ip_addr_check (u32_t, struct wordlist *); + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +static int scan_authfile (FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *); +static void free_wordlist (struct wordlist *); +static void auth_script (char *); +static void auth_script_done (void *); +static void set_allowed_addrs (int unit, struct wordlist *addrs); +static int some_ip_ok (struct wordlist *); +static int setupapfile (char **); +static int privgroup (char **); +static int set_noauth_addr (char **); +static void check_access (FILE *, char *); +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * Authentication-related options. + */ +option_t auth_options[] = { + { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "refuse-pap", o_bool, &refuse_pap, + "Don't agree to auth to peer with PAP", 1 }, + { "-pap", o_bool, &refuse_pap, + "Don't allow PAP authentication with peer", 1 }, + { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "+chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "refuse-chap", o_bool, &refuse_chap, + "Don't agree to auth to peer with CHAP", 1 }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", 1 }, + { "name", o_string, our_name, + "Set local name for authentication", + OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN }, + { "user", o_string, user, + "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN }, + { "usehostname", o_bool, &usehostname, + "Must use hostname for authentication", 1 }, + { "remotename", o_string, remote_name, + "Set remote name for authentication", OPT_STATIC, + &explicit_remote, MAXNAMELEN }, + { "auth", o_bool, &auth_required, + "Require authentication from peer", 1 }, + { "noauth", o_bool, &auth_required, + "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip }, + { "login", o_bool, &uselogin, + "Use system password database for PAP", 1 }, + { "papcrypt", o_bool, &cryptpap, + "PAP passwords are encrypted", 1 }, + { "+ua", o_special, (void *)setupapfile, + "Get PAP user and password from file" }, + { "password", o_string, passwd, + "Password for authenticating us to the peer", OPT_STATIC, + NULL, MAXSECRETLEN }, + { "privgroup", o_special, (void *)privgroup, + "Allow group members to use privileged options", OPT_PRIV }, + { "allow-ip", o_special, (void *)set_noauth_addr, + "Set IP address(es) which can be used without authentication", + OPT_PRIV }, + { NULL } +}; +#endif /* UNUSED */ +#if 0 /* UNUSED */ +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(char **argv) +{ + FILE * ufile; + int l; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + seteuid(getuid()); + ufile = fopen(*argv, "r"); + seteuid(0); + if (ufile == NULL) { + option_error("unable to open user login data file %s", *argv); + return 0; + } + check_access(ufile, *argv); + + /* get username */ + if (fgets(user, MAXNAMELEN - 1, ufile) == NULL + || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){ + option_error("unable to read user login data file %s", *argv); + return 0; + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(user); + if (l > 0 && user[l-1] == '\n') + user[l-1] = 0; + l = strlen(passwd); + if (l > 0 && passwd[l-1] == '\n') + passwd[l-1] = 0; + + return (1); +} +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* + * privgroup - allow members of the group to have privileged access. + */ +static int +privgroup(char **argv) +{ + struct group *g; + int i; + + g = getgrnam(*argv); + if (g == 0) { + option_error("group %s is unknown", *argv); + return 0; + } + for (i = 0; i < ngroups; ++i) { + if (groups[i] == g->gr_gid) { + privileged = 1; + break; + } + } + return 1; +} +#endif + +#if 0 /* UNUSED */ +/* + * set_noauth_addr - set address(es) that can be used without authentication. + * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. + */ +static int +set_noauth_addr(char **argv) +{ + char *addr = *argv; + int l = strlen(addr); + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1); + if (wp == NULL) + novm("allow-ip argument"); + wp->word = (char *) (wp + 1); + wp->next = noauth_addrs; + BCOPY(addr, wp->word, l); + noauth_addrs = wp; + return 1; +} +#endif /* UNUSED */ + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(int unit) +{ + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit)); +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(int unit) +{ + AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + plogout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n")); + pppLinkTerminated(unit); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(int unit) +{ + int i; + struct protent *protp; + + AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit)); + + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; + } + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); + } + } + num_np_open = 0; /* number of network protocols we have opened */ + num_np_up = 0; /* Number of network protocols which have come up */ + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + pppLinkDown(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(int unit) +{ + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n")); + } + } + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(int unit) +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } + } + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } +} +/* @todo: add void start_networks(void) here (pppd 2.3.11) */ + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(int unit, u16_t protocol) +{ + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + + +#if PAP_SUPPORT || CHAP_SUPPORT +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_PEER; + break; + case PPP_PAP: + pbit = PAP_PEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > (int)sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(int unit, u16_t protocol) +{ + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ + pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + lcp_close(unit, "Failed to authenticate ourselves to peer"); +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(int unit, u16_t protocol) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); + } + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(void *arg) +{ + struct ppp_idle idle; + u_short itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + AUTHDEBUG(LOG_INFO, ("Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +#if 0 /* UNUSED */ +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options(void) +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } +} +#endif /* UNUSED */ + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(int unit) +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; + } + } +} + +#if PAP_SUPPORT +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +u_char +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) +{ +#if 1 /* XXX Assume all entries OK. */ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ +#else + u_char ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u_short attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ + + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + /* @todo: this was sleep(), i.e. seconds, not milliseconds + * I don't think we really need this in lwIP - we would block tcpip_thread! + */ + /*sys_msleep((attempts - 3) * 5);*/ + } + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +#endif +} +#endif /* PAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * This function is needed for PAM. + */ + +#ifdef USE_PAM + +/* lwip does not support PAM*/ + +#endif /* USE_PAM */ + +#endif /* UNUSED */ + + +#if 0 /* UNUSED */ +/* + * plogin - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +plogin(char *user, char *passwd, char **msg, int *msglen) +{ + + LWIP_UNUSED_ARG(user); + LWIP_UNUSED_ARG(passwd); + LWIP_UNUSED_ARG(msg); + LWIP_UNUSED_ARG(msglen); + + + /* The new lines are here align the file when + * compared against the pppd 2.3.11 code */ + + + + + + + + + + + + + + + + + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); +} +#endif + + + +/* + * plogout - Logout the user. + */ +static void +plogout(void) +{ + logged_in = 0; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; +} + + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_pap_passwd(int unit, char *user, char *passwd) +{ + LWIP_UNUSED_ARG(unit); +/* normally we would reject PAP if no password is provided, + but this causes problems with some providers (like CHT in Taiwan) + who incorrectly request PAP and expect a bogus/empty password, so + always provide a default user/passwd of "none"/"none" + + @todo: This should be configured by the user, instead of being hardcoded here! +*/ + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(void) +{ + /* XXX Fail until we set up our passwords. */ + return 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(char *client, char *server, u32_t remote) +{ + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + + /* XXX Fail until we set up our passwords. */ + return 0; +} +#if CHAP_SUPPORT + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) +{ +#if 1 + int len; + struct wordlist *addrs; + + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; + + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = (int)strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; +#else + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } + + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} +#endif /* CHAP_SUPPORT */ + + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(int unit, struct wordlist *addrs) +{ + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; + +#if 0 + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } + } + } +#endif +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(int unit, u32_t addr) +{ + return ip_addr_check(addr, addresses[unit]); +} + +static int /* @todo: integrate this funtion into auth_ip_addr()*/ +ip_addr_check(u32_t addr, struct wordlist *addrs) +{ + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(u32_t addr) +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * some_ip_ok - check a wordlist to see if it authorizes any + * IP address(es). + */ +static int +some_ip_ok(struct wordlist *addrs) +{ + for (; addrs != 0; addrs = addrs->next) { + if (addrs->word[0] == '-') + break; + if (addrs->word[0] != '!') + return 1; /* some IP address is allowed */ + } + return 0; +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +static void +check_access(FILE *f, char *filename) +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + warn("cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + warn("Warning - secret file %s has world and/or group access", + filename); + } +} + + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line up to a "--" (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. Any + * following words (extra options) are placed in a wordlist and + * returned in *opts. + * We assume secret is NULL or points to MAXWORDLEN bytes of space. + */ +static int +scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename) +{ + /* We do not (currently) need this in lwip */ + return 0; /* dummy */ +} +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(struct wordlist *wp) +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} + +/* + * auth_script_done - called when the auth-up or auth-down script + * has finished. + */ +static void +auth_script_done(void *arg) +{ + auth_script_pid = 0; + switch (auth_script_state) { + case s_up: + if (auth_state == s_down) { + auth_script_state = s_down; + auth_script(_PATH_AUTHDOWN); + } + break; + case s_down: + if (auth_state == s_up) { + auth_script_state = s_up; + auth_script(_PATH_AUTHUP); + } + break; + } +} + +/* + * auth_script - execute a script with arguments + * interface-name peer-name real-user tty speed + */ +static void +auth_script(char *script) +{ + char strspeed[32]; + struct passwd *pw; + char struid[32]; + char *user_name; + char *argv[8]; + + if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL) + user_name = pw->pw_name; + else { + slprintf(struid, sizeof(struid), "%d", getuid()); + user_name = struid; + } + slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); + + argv[0] = script; + argv[1] = ifname; + argv[2] = peer_authname; + argv[3] = user_name; + argv[4] = devnam; + argv[5] = strspeed; + argv[6] = NULL; + + auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL); +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.h new file mode 100644 index 0000000..a8069ec --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/auth.h @@ -0,0 +1,111 @@ +/***************************************************************************** +* auth.h - PPP Authentication and phase control header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD pppd.h. +*****************************************************************************/ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); + +/* peer successfully authenticated itself */ +void auth_peer_success (int, u16_t, char *, int); + +/* we failed to authenticate ourselves */ +void auth_withpeer_fail (int, u16_t); + +/* we successfully authenticated ourselves */ +void auth_withpeer_success (int, u16_t); + +/* check authentication options supplied */ +void auth_check_options (void); + +/* check what secrets we have */ +void auth_reset (int); + +/* Check peer-supplied username/password */ +u_char check_passwd (int, char *, int, char *, int, char **, int *); + +/* get "secret" for chap */ +int get_secret (int, char *, char *, char *, int *, int); + +/* check if IP address is authorized */ +int auth_ip_addr (int, u32_t); + +/* check if IP address is unreasonable */ +int bad_ip_adrs (u32_t); + +#endif /* AUTH_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.c new file mode 100644 index 0000000..f10e27d --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.c @@ -0,0 +1,908 @@ +/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/ +/***************************************************************************** +* chap.c - Network Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap.c. +*****************************************************************************/ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "magic.h" +#include "randm.h" +#include "auth.h" +#include "md5.h" +#include "chap.h" +#include "chpms.h" + +#include + +#if 0 /* UNUSED */ +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap[0].timeouttime, + "Set timeout for CHAP" }, + { "chap-max-challenge", o_int, &chap[0].max_transmits, + "Set max #xmits for challenge" }, + { "chap-interval", o_int, &chap[0].chal_interval, + "Set interval for rechallenge" }, +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif + { NULL } +}; +#endif /* UNUSED */ + +/* + * Protocol entry points. + */ +static void ChapInit (int); +static void ChapLowerUp (int); +static void ChapLowerDown (int); +static void ChapInput (int, u_char *, int); +static void ChapProtocolReject (int); +#if PPP_ADDITIONAL_CALLBACKS +static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + ChapPrintPkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "CHAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout (void *); +static void ChapResponseTimeout (void *); +static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int); +static void ChapRechallenge (void *); +static void ChapReceiveResponse (chap_state *, u_char *, int, int); +static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapSendStatus (chap_state *, int); +static void ChapSendChallenge (chap_state *); +static void ChapSendResponse (chap_state *); +static void ChapGenChallenge (chap_state *); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(int unit) +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(int unit) +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(ChapChallengeTimeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(ChapRechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ + } + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(int unit, u_char *inpacket, int packet_len) +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code)); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len) +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n", + rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n", + rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(chap_state *cstate) +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = (int)strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(chap_state *cstate, int code) +{ + u_char *outp; + int outlen, msglen; + char msg[256]; /* @todo: this can be a char*, no strcpy needed */ + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = (int)strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(chap_state *cstate) +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff); + cstate->chal_len = (u_char)chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic() & 0xff); + } +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(chap_state *cstate) +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = (int)strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static int +ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) { + printer(arg, " %s", ChapCodenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.h new file mode 100644 index 0000000..fedcab8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chap.h @@ -0,0 +1,150 @@ +/***************************************************************************** +* chap.h - Network Challenge Handshake Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-03 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $ + */ + +#ifndef CHAP_H +#define CHAP_H + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +extern chap_state chap[]; + +void ChapAuthWithPeer (int, char *, u_char); +void ChapAuthPeer (int, char *, u_char); + +extern struct protent chap_protent; + +#endif /* CHAP_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.c new file mode 100644 index 0000000..81a887b --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.c @@ -0,0 +1,396 @@ +/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/ +/*** The original PPPD code is written in a way to require either the UNIX DES + encryption functions encrypt(3) and setkey(3) or the DES library libdes. + Since both is not included in lwIP, MSCHAP currently does not work! */ +/***************************************************************************** +* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap_ms.c. +*****************************************************************************/ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#define USE_CRYPT + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md4.h" +#ifndef USE_CRYPT +#include "des.h" +#endif +#include "chap.h" +#include "chpms.h" + +#include + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ + +/* XXX Don't know what to do with these. */ +extern void setkey(const char *); +extern void encrypt(char *, int); + +static void DesEncrypt (u_char *, u_char *, u_char *); +static void MakeKey (u_char *, u_char *); + +#ifdef USE_CRYPT +static void Expand (u_char *, u_char *); +static void Collapse (u_char *, u_char *); +#endif + +static void ChallengeResponse( + u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */ +); +static void ChapMS_NT( + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response +); +static u_char Get7Bits( + u_char *input, + int startBit +); + +static void +ChallengeResponse( u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */) +{ + u_char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey((char*)crypt_key); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt((char*)des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char +Get7Bits( u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void +Expand(u_char *in, u_char *out) +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } +} + +/* The inverse of Expand + */ +static void +Collapse(u_char *in, u_char *out) +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } +} +#endif + +static void +MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */ + u_char *des_key /* OUT 64 bit DES key with parity bits added */) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + MDstruct md4Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + LWIP_UNUSED_ARG(rchallenge_len); + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u_char)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (PP_HTONS((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + /* @todo: arg type - u_long* or u_int* ? */ + MDreverse((unsigned int*)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u_char)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* MSCHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.h new file mode 100644 index 0000000..df070fb --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/chpms.h @@ -0,0 +1,64 @@ +/***************************************************************************** +* chpms.h - Network Microsoft Challenge Handshake Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-01-30 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef CHPMS_H +#define CHPMS_H + +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS (chap_state *, char *, int, char *, int); + +#endif /* CHPMS_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.c new file mode 100644 index 0000000..e8a254e --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.c @@ -0,0 +1,890 @@ +/***************************************************************************** +* fsm.c - Network Control Protocol Finite State Machine program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD fsm.c. +*****************************************************************************/ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" + +#include + +#if PPP_DEBUG +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; +#endif /* PPP_DEBUG */ + +static void fsm_timeout (void *); +static void fsm_rconfreq (fsm *, u_char, u_char *, int); +static void fsm_rconfack (fsm *, int, u_char *, int); +static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); +static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rtermack (fsm *); +static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_sconfreq (fsm *, int); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +int peer_mru[NUM_PPP]; + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(fsm *f) +{ + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; + + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + +#if 0 /* backport pppd 2.4.4b1; */ +/* + * terminate_layer - Start process of shutting down the FSM + * + * Cancel any timeout running, notify upper layers we're done, and + * send a terminate-request message as configured. + */ +static void +terminate_layer(fsm *f, int nextstate) +{ + /* @todo */ +} +#endif + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the LS_CLOSED state. + */ +void +fsm_close(fsm *f, char *reason) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(void *arg) +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(fsm *f, u_char *inpacket, int l) +{ + u_char *inp = inpacket; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f))); + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +{ + int code, reject_if_disagree; + + FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u_char)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(fsm *f, int id, u_char *inp, int len) +{ + FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +{ + int (*proc) (fsm *, u_char *, int); + int ret; + + FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(fsm *f, int id, u_char *p, int len) +{ + LWIP_UNUSED_ARG(p); + + FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(fsm *f) +{ + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + default: + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(fsm *f, u_char *inp, int len) +{ + u_char code, id; + + FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(fsm *f) +{ + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(fsm *f, int retransmit) +{ + u_char *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); +} + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.h new file mode 100644 index 0000000..8d41b5f --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/fsm.h @@ -0,0 +1,157 @@ +/***************************************************************************** +* fsm.h - Network Control Protocol Finite State Machine header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD code. +*****************************************************************************/ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $ + */ + +#ifndef FSM_H +#define FSM_H + +/* + * LCP Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Prototypes + */ +void fsm_init (fsm*); +void fsm_lowerup (fsm*); +void fsm_lowerdown (fsm*); +void fsm_open (fsm*); +void fsm_close (fsm*, char*); +void fsm_input (fsm*, u_char*, int); +void fsm_protreject (fsm*); +void fsm_sdata (fsm*, u_char, u_char, u_char*, int); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ + +#endif /* FSM_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.c new file mode 100644 index 0000000..f0ab2e0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.c @@ -0,0 +1,1411 @@ +/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and + dial-on-demand has been stripped. */ +/***************************************************************************** +* ipcp.c - Network PPP IP Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "fsm.h" +#include "vj.h" +#include "ipcp.h" + +#include "lwip/inet.h" + +#include + +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ + +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +/* local vars */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ + + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ +#if PPP_ADDITIONAL_CALLBACKS +static void ipcp_script (fsm *, char *); /* Run an up/down script */ +#endif +static void ipcp_finished (fsm *); /* Don't need lower layer */ + + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Protocol entry points from main code. + */ +static void ipcp_init (int); +static void ipcp_open (int); +static void ipcp_close (int, char *); +static void ipcp_lowerup (int); +static void ipcp_lowerdown (int); +static void ipcp_input (int, u_char *, int); +static void ipcp_protrej (int); + + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if PPP_ADDITIONAL_CALLBACKS + ipcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "IPCP", +#if PPP_ADDITIONAL_CALLBACKS + ip_check_options, + NULL, + ip_active_pkt +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +static void ipcp_clear_addrs (int); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(int unit) +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(int unit) +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(int unit, char *reason) +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(int unit) +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(int unit, u_char *p, int len) +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(fsm *f) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(fsm *f) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG(LOG_INFO, ("remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; +#ifdef OLD_CI_ADDRS + ipcp_options *go = &ipcp_gotoptions[f->unit]; +#endif + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ +#ifdef OLD_CI_ADDRS + u32_t ciaddr2; /* Parsed address values */ +#endif + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = (u_short)l;/* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +#ifdef OLD_CI_ADDRS /* Need to save space... */ + case CI_ADDRS: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; +#endif + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG(LOG_INFO, ( + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options(u_long localAddr) +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } +} +#endif + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(fsm *f) +{ + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG(LOG_INFO, ("ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG(LOG_WARNING, ("sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(fsm *f) +{ + IPCPDEBUG(LOG_INFO, ("ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, etc. + */ +static void +ipcp_clear_addrs(int unit) +{ + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(fsm *f) +{ + np_finished(f->unit, PPP_IP); +} + +#if PPP_ADDITIONAL_CALLBACKS +static int +ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(u_char *pkt, int len) +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.h new file mode 100644 index 0000000..de03f46 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ipcp.h @@ -0,0 +1,106 @@ +/***************************************************************************** +* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef IPCP_H +#define IPCP_H + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ + +typedef struct ipcp_options { + u_int neg_addr : 1; /* Negotiate IP Address? */ + u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ + u_int req_addr : 1; /* Ask peer to send IP address? */ + u_int default_route : 1; /* Assign default route through interface? */ + u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u_int neg_vj : 1; /* Van Jacobson Compression? */ + u_int old_vj : 1; /* use old (short) form of VJ option? */ + u_int accept_local : 1; /* accept peer's value for ouraddr */ + u_int accept_remote : 1; /* accept peer's value for hisaddr */ + u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex; /* VJ slots - 1. */ + u_char cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +extern struct protent ipcp_protent; + +#endif /* IPCP_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.c new file mode 100644 index 0000000..54f758a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.c @@ -0,0 +1,2066 @@ +/***************************************************************************** +* lcp.c - Network Link Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "chap.h" +#include "magic.h" +#include "auth.h" +#include "lcp.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + +#if 0 /* UNUSED */ +/* + * LCP-related command-line options. + */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +bool lax_recv = 0; /* accept control chars in asyncmap */ + +static int setescape (char **); + +static option_t lcp_option_list[] = { + /* LCP options */ + /* list stripped for simplicity */ + {NULL} +}; +#endif /* UNUSED */ + +/* options */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ + +/* global vars */ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ + +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */ +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u_char, u_char*, int); +static void lcp_rprotrej (fsm*, u_char*, int); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup (int); +static void lcp_echo_lowerdown (int); +static void LcpEchoTimeout (void*); +static void lcp_received_echo_reply (fsm*, int, u_char*, int); +static void LcpSendEchoRequest (fsm*); +static void LcpLinkFailure (fsm*); +static void LcpEchoCheck (fsm*); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_input (int, u_char *, int); +static void lcp_protrej (int); + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if PPP_ADDITIONAL_CALLBACKS + lcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "LCP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED */ +/* + * setescape - add chars to the set we escape on transmission. + */ +static int +setescape(argv) + char **argv; +{ + int n, ret; + char *p, *endp; + + p = *argv; + ret = 1; + while (*p) { + n = strtol(p, &endp, 16); + if (p == endp) { + option_error("escape parameter contains invalid hex number '%s'", p); + return 0; + } + p = endp; + if (n < 0 || n == 0x5E || n > 0xFF) { + option_error("can't escape character 0x%x", n); + ret = 0; + } else + xmit_accm[0][n >> 5] |= 1 << (n & 0x1F); + while (*p == ',' || *p == ' ') + ++p; + } + return ret; +} +#endif /* UNUSED */ + +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(int unit, char *reason) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(f, reason); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(int unit) +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0] + | ((u_long)xmit_accm[unit][1] << 8) + | ((u_long)xmit_accm[unit][2] << 16) + | ((u_long)xmit_accm[unit][3] << 24); + LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(int unit) +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(int unit, u_char *p, int len) +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(fsm *f, u_char *inp, int len) +{ + int i; + struct protent *protp; + u_short prot; + + if (len < (int)sizeof (u_short)) { + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot)); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +static void +lcp_protrej(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(int unit, u_char *p, int len) +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(fsm *f) +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n")); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n")); + return (1); +bad: + LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(fsm *f, + u_char *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype; /* Parsed len, type */ + u_char cichar; /* Parsed char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ +#if TRACELCP > 0 + char traceBuf[80]; + size_t traceNdx = 0; +#endif + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#if MSCHAP_SUPPORT + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); +#endif + + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); +#endif + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + default: +#if TRACELCP + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } +#endif + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + +#if TRACELCP > 0 + if (traceNdx > 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf)); + } +#endif + LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(fsm *f) +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); /* The link is up; authenticate now */ +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(fsm *f) +{ + link_required(f->unit); /* lwip: currently does nothing */ +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(fsm *f) +{ + link_terminated(f->unit); /* we are finished with the link */ +} + + +#if PPP_ADDITIONAL_CALLBACKS +/* + * print_string - print a readable representation of a string using + * printer. + */ +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } + } + printer(arg, "\""); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +/* + * Time to shut down the link because there is nothing out there. + */ +static void +LcpLinkFailure (fsm *f) +{ + if (f->state == LS_OPENED) { + LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ +static void +LcpEchoCheck (fsm *f) +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ +static void +LcpEchoTimeout (void *arg) +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ +static void +lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +{ + u32_t magic; + + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ +static void +LcpSendEchoRequest (fsm *f) +{ + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + ++lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + LcpEchoCheck (f); + } +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.h new file mode 100644 index 0000000..b9201ee --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/lcp.h @@ -0,0 +1,151 @@ +/***************************************************************************** +* lcp.h - Network Link Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef LCP_H +#define LCP_H +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ + +/* + * LCP-specific packet types (code numbers). + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + u_int passive : 1; /* Don't die if we don't get a response */ + u_int silent : 1; /* Wait for the other end to start first */ + u_int restart : 1; /* Restart vs. exit after close */ + u_int neg_mru : 1; /* Negotiate the MRU? */ + u_int neg_asyncmap : 1; /* Negotiate the async map? */ + u_int neg_upap : 1; /* Ask for UPAP authentication? */ + u_int neg_chap : 1; /* Ask for CHAP authentication? */ + u_int neg_magicnumber : 1; /* Ask for magic number? */ + u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_int neg_cbcp : 1; /* Negotiate use of CBCP */ +#ifdef PPP_MULTILINK + u_int neg_mrru : 1; /* Negotiate multilink MRRU */ + u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ +#endif + u_short mru; /* Value of MRU */ +#ifdef PPP_MULTILINK + u_short mrru; /* Value of MRRU, and multilink enable */ +#endif + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#ifdef PPP_MULTILINK + struct epdisc endpoint; /* endpoint discriminator */ +#endif +} lcp_options; + +/* + * Values for phase from BSD pppd.h based on RFC 1661. + */ +typedef enum { + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE +} LinkPhase; + + + +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern ext_accm xmit_accm[]; + + +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 + +#endif /* LCP_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.c new file mode 100644 index 0000000..3732a42 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.c @@ -0,0 +1,80 @@ +/***************************************************************************** +* magic.c - Network Random Number Generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD magic.c. +*****************************************************************************/ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "ppp_impl.h" +#include "randm.h" +#include "magic.h" + + +/* + * magicInit - Initialize the magic number generator. + * + * Since we use another random number generator that has its own + * initialization, we do nothing here. + */ +void magicInit() +{ + return; +} + +/* + * magic - Returns the next magic number. + */ +u32_t magic() +{ + return avRandom(); +} + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.h new file mode 100644 index 0000000..eba70d2 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/magic.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* magic.h - Network Random Number Generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef MAGIC_H +#define MAGIC_H + +/* Initialize the magic number generator */ +void magicInit(void); + +/* Returns the next magic number */ +u32_t magic(void); + +#endif /* MAGIC_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.c new file mode 100644 index 0000000..dc3cc75 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.c @@ -0,0 +1,320 @@ +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md5.h" + +#include + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (u32_t *buf, u32_t *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##UL +#else +#ifdef WIN32 +#define UL(x) x##UL +#else +#define UL(x) x +#endif +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (u32_t)0; + + /* Load magic initialization constants. */ + mdContext->buf[0] = (u32_t)0x67452301UL; + mdContext->buf[1] = (u32_t)0xefcdab89UL; + mdContext->buf[2] = (u32_t)0x98badcfeUL; + mdContext->buf[3] = (u32_t)0x10325476UL; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + +#if 0 + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf)); + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf)); +#endif + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) { + mdContext->i[1]++; + } + mdContext->i[0] += ((u32_t)inLen << 3); + mdContext->i[1] += ((u32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +MD5Final (unsigned char hash[], MD5_CTX *mdContext) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + SMEMCPY(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void +Transform (u32_t *buf, u32_t *in) +{ + u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.h new file mode 100644 index 0000000..e129533 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/md5.h @@ -0,0 +1,55 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init ( MD5_CTX *mdContext); +void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final ( unsigned char hash[], MD5_CTX *mdContext); + +#endif /* MD5_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.c new file mode 100644 index 0000000..5fb9f88 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.c @@ -0,0 +1,628 @@ +/***************************************************************************** +* pap.c - Network Password Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-12 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "pap.h" + +#include + +#if 0 /* UNUSED */ +static bool hide_password = 1; + +/* + * Command-line options. + */ +static option_t pap_option_list[] = { + { "hide-password", o_bool, &hide_password, + "Don't output passwords to log", 1 }, + { "show-password", o_bool, &hide_password, + "Show password string in debug log messages", 0 }, + { "pap-restart", o_int, &upap[0].us_timeouttime, + "Set retransmit timeout for PAP" }, + { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, + "Set max number of transmissions for auth-reqs" }, + { "pap-timeout", o_int, &upap[0].us_reqtimeout, + "Set time limit for peer PAP authentication" }, + { NULL } +}; +#endif + +/* + * Protocol entry points. + */ +static void upap_init (int); +static void upap_lowerup (int); +static void upap_lowerdown (int); +static void upap_input (int, u_char *, int); +static void upap_protrej (int); +#if PPP_ADDITIONAL_CALLBACKS +static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + upap_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "PAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u_char *, u_char, int); +static void upap_rauthack (upap_state *, u_char *, int, int); +static void upap_rauthnak (upap_state *, u_char *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u_char, u_char, char *, int); + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(int unit, char *user, char *password) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = (int)strlen(user); + u->us_passwd = password; + u->us_passwdlen = (int)strlen(password); + + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(int unit) +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + /* now client state is UPAPCS__AUTHREQ */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(int unit) +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(int unit, u_char *inpacket, int l) +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len)); + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len) +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + u_char retcode; + char *msg; + int msglen; + + UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + /* lwip: currently retcode is always UPAP_AUTHACK */ + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n")); + return; + } + + /* + * Parse message. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nak. + */ +static void +upap_rauthnak(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if(msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(upap_state *u) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static int upap_printpkt( + u_char *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg +) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.h new file mode 100644 index 0000000..c99a204 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pap.h @@ -0,0 +1,118 @@ +/***************************************************************************** +* pap.h - PPP Password Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef PAP_H +#define PAP_H + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +extern upap_state upap[]; + +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); + +extern struct protent pap_protent; + +#endif /* PAP_SUPPORT */ + +#endif /* PAP_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.c new file mode 100644 index 0000000..2a34657 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.c @@ -0,0 +1,2052 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "lwip/ip.h" /* for ip_input() */ + +#include "pppdebug.h" + +#include "randm.h" +#include "fsm.h" +#if PAP_SUPPORT +#include "pap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap.h" +#endif /* CHAP_SUPPORT */ +#include "ipcp.h" +#include "lcp.h" +#include "magic.h" +#include "auth.h" +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include "lwip/tcpip.h" +#include "lwip/api.h" +#include "lwip/snmp.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback(). + * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1. + * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded). + */ +#ifndef PPP_INPROC_MULTITHREADED +#define PPP_INPROC_MULTITHREADED (NO_SYS==0) +#endif + +/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session. + * Default is 0: call pppos_input() for received raw characters, charcater + * reception is up to the port */ +#ifndef PPP_INPROC_OWNTHREAD +#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED +#endif + +#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED + #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1" +#endif + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} PPPDevStates; + +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + +/** RX buffer size: this may be configured smaller! */ +#ifndef PPPOS_RX_BUFSIZE +#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN) +#endif + +typedef struct PPPControlRx_s { + /** unit number / ppp descriptor */ + int pd; + /** the rx file descriptor */ + sio_fd_t fd; + /** receive buffer - encoded data is stored here */ +#if PPP_INPROC_OWNTHREAD + u_char rxbuf[PPPOS_RX_BUFSIZE]; +#endif /* PPP_INPROC_OWNTHREAD */ + + /* The input packet. */ + struct pbuf *inHead, *inTail; + +#if PPPOS_SUPPORT + u16_t inProtocol; /* The input protocol code. */ + u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + PPPDevStates inState; /* The input process state. */ + char inEscaped; /* Escape next character. */ + ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ +} PPPControlRx; + +/* + * PPP interface control block. + */ +typedef struct PPPControl_s { + PPPControlRx rx; + char openFlag; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ +#endif /* PPPOS_SUPPORT */ + u16_t mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u_long lastXMit; /* Time of last transmission. */ + ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vjEnabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vjComp; /* Van Jacobson compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + struct netif netif; + + struct ppp_addrs addrs; + + void (*linkStatusCB)(void *ctx, int errCode, void *arg); + void *linkStatusCtx; + +} PPPControl; + + +/* + * Ioctl definitions. + */ + +struct npioctl { + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum NPmode mode; +}; + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +#if PPPOS_SUPPORT +#if PPP_INPROC_OWNTHREAD +static void pppInputThread(void *arg); +#endif /* PPP_INPROC_OWNTHREAD */ +static void pppDrop(PPPControlRx *pcrx); +static void pppInProc(PPPControlRx *pcrx, u_char *s, int l); +static void pppFreeCurrentInputPacket(PPPControlRx *pcrx); +#endif /* PPPOS_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +struct protent *ppp_protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL +}; + + +/* + * Buffers for outgoing packets. This must be accessed only from the appropriate + * PPP task so that it doesn't need to be protected to avoid collisions. + */ +u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ + +#if PPPOS_SUPPORT +/* + * FCS lookup table as calculated by genfcstab. + * @todo: smaller, slower implementation for lower memory footprint? + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char pppACCMMask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; + +#if PPP_INPROC_OWNTHREAD +/** Wake up the task blocked in reading from serial line (if any) */ +static void +pppRecvWakeup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd)); + if (pppControl[pd].openFlag != 0) { + sio_read_abort(pppControl[pd].fd); + } +} +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPControl* pc; +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ + pc = &pppControl[pd]; + + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } + + pc->openFlag = 0;/**/ +#endif /* PPPOS_SUPPORT */ + } + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n")); +} + +void +pppLinkDown(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/ + } +} + +/** Initiate LCP open request */ +static void +pppStart(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ + PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n")); +} + +/** LCP close request */ +static void +pppStop(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +/** Called when carrier/link is lost */ +static void +pppHup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* Initialize the PPP subsystem. */ + +struct ppp_settings ppp_settings; + +void +pppInit(void) +{ + struct protent *protp; + int i, j; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + + magicInit(); + + for (i = 0; i < NUM_PPP; i++) { + /* Initialize each protocol to the standard option set. */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); + } + } +} + +void +pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) +{ + switch(authType) { + case PPPAUTHTYPE_NONE: + default: +#ifdef LWIP_PPP_STRICT_PAP_REJECT + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; + + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if(user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if(passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } +} + +#if PPPOS_SUPPORT +/** Open a new PPP connection using the given I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. If this port + * connects to a modem, the modem connection must be + * established before calling this. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + * + * pppOpen() is directly defined to this function. + */ +int +pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + /* input pbuf left over from last session? */ + pppFreeCurrentInputPacket(&pc->rx); + /* @todo: is this correct or do I overwrite something? */ + memset(pc, 0, sizeof(PPPControl)); + pc->rx.pd = pd; + pc->rx.fd = fd; + + pc->openFlag = 1; + pc->fd = fd; + +#if VJ_SUPPORT + vj_compress_init(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */ + pc->outACCM[15] = 0x60; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd)); + pppStart(pd); +#if PPP_INPROC_OWNTHREAD + sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); +#endif /* PPP_INPROC_OWNTHREAD */ + } + + return pd; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void +pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + memset(pc, 0, sizeof(PPPControl)); + pc->openFlag = 1; + pc->ethif = ethif; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +/* Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. */ +int +pppClose(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + PPPDEBUG(LOG_DEBUG, ("pppClose() called\n")); + + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +pppSigHUP(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + pppHup(pd); +} + +#if PPPOS_SUPPORT +static void +nPut(PPPControl *pc, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG(LOG_WARNING, + ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c)); + LINK_STATS_INC(link.err); + pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ + snmp_inc_ifoutdiscards(&pc->netif); + pbuf_free(nb); + return; + } + } + + snmp_add_ifoutoctets(&pc->netif, nb->tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * pppAppend - append given character to end of given pbuf. If outACCM + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (outACCM && ESCAPE_P(*outACCM, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + u16_t tot_len; + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + tot_len = pb->tot_len; + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + +/* Send a packet on the given connection. */ +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr) +{ + int pd = (int)(size_t)netif->state; + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_short protocol = PPP_IP; + u_int fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u_char c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, PPP_IP, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vjEnabled) { + switch (vj_compress_tcp(&pc->vjComp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + pbuf_free(headMB); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->lastXMit = sys_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol)); + + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return ERR_OK; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +pppIOCtl(int pd, int cmd, void *arg) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->errCode = *(int *)arg; + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->errCode); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: /* Get the fd associated with the ppp */ + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; + } + } + + return st; +} + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short +pppMTU(int pd) +{ + PPPControl *pc = &pppControl[pd]; + u_short st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + MEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, (u16_t)n); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int +pppWrite(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_char c; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->lastXMit = sys_jiffies(); + + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len)); + /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n", + unit, + pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(int unit, ext_accm *accm) +{ + SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm)); + PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", + unit, + pppControl[unit].outACCM[0], + pppControl[unit].outACCM[1], + pppControl[unit].outACCM[2], + pppControl[unit].outACCM[3])); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ + SYS_ARCH_PROTECT(lev); + for (i = 0; i < 32 / 8; i++) { + /* @todo: does this work? ext_accm has been modified from pppd! */ + pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8)); + } + SYS_ARCH_UNPROTECT(lev); + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n", + unit, + pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3])); +} + +#if 0 +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr) +{ + return 0; /* XXX Currently no compression. */ +} + +/* + * ccp_flags_set - inform kernel about the current state of CCP. + */ +void +ccp_flags_set(int unit, int isopen, int isup) +{ + /* XXX */ +} + +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(int unit) +{ + /* XXX */ + return 0; +} +#endif + +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); + + return 0; +} + + +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t +GetMask(u32_t addr) +{ + u32_t mask, nmask; + + addr = htonl(addr); + if (IP_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IP_CLASSA_NET; + } else if (IP_CLASSB(addr)) { + nmask = IP_CLASSB_NET; + } else { + nmask = IP_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = PP_HTONL(0xffffff00UL) | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid) +{ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPControl *pc = &pppControl[pd]; + + pc->vjEnabled = vjcomp; + pc->vjComp.compressSlot = cidcomp; + pc->vjComp.maxSlotIndex = maxcid; + PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + LWIP_UNUSED_ARG(pd); + LWIP_UNUSED_ARG(vjcomp); + LWIP_UNUSED_ARG(cidcomp); + LWIP_UNUSED_ARG(maxcid); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +/* + * pppifNetifInit - netif init callback + */ +static err_t +pppifNetifInit(struct netif *netif) +{ + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = pppMTU((int)(size_t)netif->state); + netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP; +#if LWIP_NETIF_HOSTNAME + /* @todo: Initialize interface hostname */ + /* netif_set_hostname(netif, "lwip"); */ +#endif /* LWIP_NETIF_HOSTNAME */ + return ERR_OK; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, + &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); + pc->if_up = 1; + pc->errCode = PPPERR_NONE; + + PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); + } + } else { + st = 0; + PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd)); + } + } + + return st; +} + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int +sifnpmode(int u, int proto, enum NPmode mode) +{ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + /* make sure the netif status callback is called */ + netif_set_down(&pc->netif); + netif_remove(&pc->netif); + PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); + } + } + return st; +} + +/** + * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS + */ +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; +} + +/** + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? + */ +int +cifaddr( int pd, u32_t o, u32_t h) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } + + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } + + return st; +} + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD +/* The main PPP process function. This implements the state machine according + * to section 4 of RFC 1661: The Point-To-Point Protocol. */ +static void +pppInputThread(void *arg) +{ + int count; + PPPControlRx *pcrx = arg; + + while (lcp_phase[pcrx->pd] != PHASE_DEAD) { + count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE); + if(count > 0) { + pppInProc(pcrx, pcrx->rxbuf, count); + } else { + /* nothing received, give other tasks a chance to run */ + sys_msleep(1); + } + } +} +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */ + +#if PPPOE_SUPPORT + +void +pppOverEthernetInitFailed(int pd) +{ + PPPControl* pc; + + pppHup(pd); + pppStop(pd); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd)); + pppStart(pd); + } else { + pppOverEthernetInitFailed(pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf * +pppSingleBuf(struct pbuf *p) +{ + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG(LOG_ERR, + ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +/** Input helper struct, must be packed since it is stored to pbuf->payload, + * which might be unaligned. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppInputHeader { + PACK_STRUCT_FIELD(int unit); + PACK_STRUCT_FIELD(u16_t proto); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +static void +pppInput(void *arg) +{ + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; + + pd = ((struct pppInputHeader *)nb->payload)->unit; + protocol = ((struct pppInputHeader *)nb->payload)->proto; + + if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + snmp_inc_ifinucastpkts(&pppControl[pd].netif); + snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd])); + goto drop; + } + } + + switch(protocol) { + case PPP_VJC_COMP: /* VJ compressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, + ("pppInput[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = pppSingleBuf(nb); + (*protp->input)(pd, nb->payload, nb->len); + PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd)); + goto out; + } + } + + /* No handler for this protocol so reject the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } +#if BYTE_ORDER == LITTLE_ENDIAN + protocol = htons(protocol); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); + lcp_sprotrej(pd, nb->payload, nb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + +out: + pbuf_free(nb); + return; +} + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +pppFreeCurrentInputPacket(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { + if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) { + pbuf_free(pcrx->inTail); + } + pbuf_free(pcrx->inHead); + pcrx->inHead = NULL; + } + pcrx->inTail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +pppDrop(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload)); +#endif + PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead)); + } + pppFreeCurrentInputPacket(pcrx); +#if VJ_SUPPORT + vj_uncompress_err(&pppControl[pcrx->pd].vjComp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); +} + +#if !PPP_INPROC_OWNTHREAD +/** Pass received raw characters to PPPoS to be decoded. This function is + * thread-safe and can be called from a dedicated RX-thread or from a main-loop. + * + * @param pd PPP descriptor index, returned by pppOpen() + * @param data received data + * @param len length of received data + */ +void +pppos_input(int pd, u_char* data, int len) +{ + pppInProc(&pppControl[pd].rx, data, len); +} +#endif + +/** + * Process a received octet string. + */ +static void +pppInProc(PPPControlRx *pcrx, u_char *s, int l) +{ + struct pbuf *nextNBuf; + u_char curChar; + u_char escaped; + SYS_ARCH_DECL_PROTECT(lev); + + PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l)); + while (l-- > 0) { + curChar = *s++; + + SYS_ARCH_PROTECT(lev); + escaped = ESCAPE_P(pcrx->inACCM, curChar); + SYS_ARCH_UNPROTECT(lev); + /* Handle special characters. */ + if (escaped) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pcrx->inEscaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pcrx->inState <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pcrx->inState < PDDATA) { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping incomplete packet %d\n", + pcrx->pd, pcrx->inState)); + LINK_STATS_INC(link.lenerr); + pppDrop(pcrx); + /* If the fcs is invalid, drop the packet. */ + } else if (pcrx->inFCS != PPP_GOODFCS) { + PPPDEBUG(LOG_INFO, + ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n", + pcrx->pd, pcrx->inFCS, pcrx->inProtocol)); + /* Note: If you get lots of these, check for UART frame errors or try different baud rate */ + LINK_STATS_INC(link.chkerr); + pppDrop(pcrx); + /* Otherwise it's a good packet so pass it on. */ + } else { + struct pbuf *inp; + /* Trim off the checksum. */ + if(pcrx->inTail->len > 2) { + pcrx->inTail->len -= 2; + + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + } else { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + + pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + inp = pcrx->inHead; + /* Packet consumed, release our references. */ + pcrx->inHead = NULL; + pcrx->inTail = NULL; +#if PPP_INPROC_MULTITHREADED + if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) { + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd)); + pbuf_free(inp); + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); + } +#else /* PPP_INPROC_MULTITHREADED */ + pppInput(inp); +#endif /* PPP_INPROC_MULTITHREADED */ + } + + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + pcrx->inState = PDADDRESS; + pcrx->inEscaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pcrx->inEscaped) { + pcrx->inEscaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pcrx->inState) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pcrx->inState = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pcrx->inState = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar)); + pcrx->inState = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pcrx->inProtocol = curChar; + pcrx->inState = PDDATA; + } else { + pcrx->inProtocol = (u_int)curChar << 8; + pcrx->inState = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pcrx->inProtocol |= curChar; + pcrx->inState = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) { + if (pcrx->inTail != NULL) { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + /* give up the inTail reference now */ + pcrx->inTail = NULL; + } + } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd)); + LINK_STATS_INC(link.memerr); + pppDrop(pcrx); + pcrx->inState = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pcrx->inHead == NULL) { + struct pppInputHeader *pih = nextNBuf->payload; + + pih->unit = pcrx->pd; + pih->proto = pcrx->inProtocol; + + nextNBuf->len += sizeof(*pih); + + pcrx->inHead = nextNBuf; + } + pcrx->inTail = nextNBuf; + } + /* Load character into buffer. */ + ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar); + } + } /* while (l-- > 0), all bytes processed */ + + avRandomize(); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void +pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + pppInput(pb); + return; + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ + +#if LWIP_NETIF_STATUS_CALLBACK +/** Set the status callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param status_callback pointer to the status callback function + * + * @see netif_set_status_callback + */ +void +ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback) +{ + netif_set_status_callback(&pppControl[pd].netif, status_callback); +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** Set the link callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param link_callback pointer to the link callback function + * + * @see netif_set_link_callback + */ +void +ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback) +{ + netif_set_link_callback(&pppControl[pd].netif, link_callback); +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.h new file mode 100644 index 0000000..08d6e62 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp.h @@ -0,0 +1,201 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + + +#ifndef __u_char_defined + +/* Type definitions for BSD code. */ +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; + +#endif + + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +struct ppp_addrs { + ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* Initialize the PPP subsystem. */ +void pppInit(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum pppAuthType { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); + +/* Link status callback function prototype */ +typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg); + +#if PPPOS_SUPPORT +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOE_SUPPORT */ + +/* for source code compatibility */ +#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int pppClose(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void pppSigHUP(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int pppIOCtl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short pppMTU(int pd); + +#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD +/* + * PPP over Serial: this is the input function to be called for received data. + * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking + * sio_read() is used, so this is deactivated. + */ +void pppos_input(int pd, u_char* data, int len); +#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */ + + +#if LWIP_NETIF_STATUS_CALLBACK +/* Set an lwIP-style status-callback for the selected PPP device */ +void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK +/* Set an lwIP-style link-callback for the selected PPP device */ +void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_impl.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_impl.h new file mode 100644 index 0000000..89aea60 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_impl.h @@ -0,0 +1,363 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_IMPL_H +#define PPP_IMPL_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + +/** Some defines for code we skip compared to the original pppd. + * These are just here to minimise the use of the ugly "#if 0". */ +#define PPP_ADDITIONAL_CALLBACKS 0 + +/** Some error checks to test for unsupported code */ +#if CBCP_SUPPORT +#error "CBCP is not supported in lwIP PPP" +#endif +#if CCP_SUPPORT +#error "CCP is not supported in lwIP PPP" +#endif + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if PPP_ADDITIONAL_CALLBACKS + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + int enabled_flag; /* 0 if protocol is disabled */ + char *name; /* Text name of protocol */ +#if PPP_ADDITIONAL_CALLBACKS + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, u16_t, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, u8_t, u8_t); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_IMPL_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_oe.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_oe.c new file mode 100644 index 0000000..fdf52ae --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1132 @@ +/***************************************************************************** +* ppp_oe.c - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp_oe.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "lwip/timers.h" +#include "lwip/memp.h" + +#include +#include + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (u8_t)((VAL) / 256); \ + *(PTR)++ = (u8_t)((VAL) % 256) + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#error "PPPOE_SERVER is not yet supported under lwIP!" +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +#ifndef PPPOE_ERRORSTRING_LEN +#define PPPOE_ERRORSTRING_LEN 64 +#endif +static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN]; + + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +/** linked list of created pppoe interfaces */ +static struct pppoe_softc *pppoe_softc_list; + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF); + if (sc == NULL) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + /* put the new interface at the head of the list */ + sc->next = pppoe_softc_list; + pppoe_softc_list = sc; + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc *sc, *prev = NULL; + + for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + sys_untimeout(pppoe_timeout, sc); + if (prev == NULL) { + /* remove sc from the head of the list */ + pppoe_softc_list = sc->next; + } else { + /* remove sc from the list */ + prev->next = sc->next; + } + +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } +#endif /* PPPOE_TODO */ + memp_free(MEMP_PPPOE_IF, sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (pppoe_softc_list == NULL) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(struct pppoe_softc *sc) +{ + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + u8_t *ac_cookie; + u16_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + if (errortag && len) { + u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1); + strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len); + pppoe_error_tmp[error_len-1] = '\0'; + printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + sys_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + (u16_t)ph->code, session); + } else { + printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len; +#ifdef PPPOE_TODO + int l1 = 0, l2 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + l1 = (int)strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = (int)strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } +#endif /* PPPOE_TODO */ + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } +#endif /* PPPOE_TODO */ + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + sys_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; +#ifdef PPPOE_TODO + size_t l1 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } +#endif /* PPPOE_TODO */ + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/pppdebug.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pppdebug.h new file mode 100644 index 0000000..8134997 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/pppdebug.h @@ -0,0 +1,73 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/* Trace levels. */ +#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_INFO (PPP_DEBUG) +#define LOG_DETAIL (PPP_DEBUG) +#define LOG_DEBUG (PPP_DEBUG) + + +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG + +#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b) +#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b) +#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b) + +#else /* PPP_DEBUG */ + +#define AUTHDEBUG(a, b) +#define IPCPDEBUG(a, b) +#define UPAPDEBUG(a, b) +#define LCPDEBUG(a, b) +#define FSMDEBUG(a, b) +#define CHAPDEBUG(a, b) +#define PPPDEBUG(a, b) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.c new file mode 100644 index 0000000..b736091 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.c @@ -0,0 +1,249 @@ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "md5.h" +#include "randm.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include + +#if MD5_SUPPORT /* this module depends on MD5 */ +#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ +static long randCount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Since this is to be called on power up, we don't have much + * system randomess to work with. Here all we use is the + * real-time clock. We'll accumulate more randomness as soon + * as things start happening. + */ +void +avRandomInit() +{ + avChurnRand(NULL, 0); +} + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +void +avChurnRand(char *randData, u32_t randLen) +{ + MD5_CTX md5; + + /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */ + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + if (randData) { + MD5Update(&md5, (u_char *)randData, randLen); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sysData; + + /* Load sysData fields here. */ + MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); + } + MD5Final((u_char *)randPool, &md5); +/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */ +} + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Note: It's important that there be sufficient randomness in randPool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call churnRand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * randCount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void +avGenRand(char *buf, u32_t bufLen) +{ + MD5_CTX md5; + u_char tmp[16]; + u32_t n; + + while (bufLen > 0) { + n = LWIP_MIN(bufLen, RANDPOOLSZ); + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); + MD5Final(tmp, &md5); + randCount++; + MEMCPY(buf, tmp, n); + buf += n; + bufLen -= n; + } +} + +/* + * Return a new random number. + */ +u32_t +avRandom() +{ + u32_t newRand; + + avGenRand((char *)&newRand, sizeof(newRand)); + + return newRand; +} + +#else /* MD5_SUPPORT */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int avRandomized = 0; /* Set when truely randomized. */ +static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void +avRandomInit() +{ +#if 0 + /* Get a pointer into the last 4 bytes of clockBuf. */ + u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + + /* + * Initialize our seed using the real-time clock, the idle + * counter, the millisecond timer, and the hardware timer + * tick counter. The real-time clock and the hardware + * tick counter are the best sources of randomness but + * since the tick counter is only 16 bit (and truncated + * at that), the idle counter and millisecond timer + * (which may be small values) are added to help + * randomize the lower 16 bits of the seed. + */ + readClk(); + avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr + + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; +#else + avRandomSeed += sys_jiffies(); /* XXX */ +#endif + + /* Initialize the Borland random number generator. */ + srand((unsigned)avRandomSeed); +} + +/* + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void +avRandomize(void) +{ + static u32_t last_jiffies; + + if (!avRandomized) { + avRandomized = !0; + avRandomInit(); + /* The initialization function also updates the seed. */ + } else { + /* avRandomSeed += (avRandomSeed << 16) + TM1; */ + avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t +avRandom() +{ + return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); +} + +#endif /* MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.h new file mode 100644 index 0000000..a0984b0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/randm.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#ifndef RANDM_H +#define RANDM_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * Initialize the random number generator. + */ +void avRandomInit(void); + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + */ +void avChurnRand(char *randData, u32_t randLen); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +#if MD5_SUPPORT +#define avRandomize() avChurnRand(NULL, 0) +#else /* MD5_SUPPORT */ +void avRandomize(void); +#endif /* MD5_SUPPORT */ + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void avGenRand(char *buf, u32_t bufLen); + +/* + * Return a new random number. + */ +u32_t avRandom(void); + + +#endif /* RANDM_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/readme.txt b/app/src/main/jni/badvpn/lwip/src/netif/ppp/readme.txt new file mode 100644 index 0000000..5be41b9 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/readme.txt @@ -0,0 +1,21 @@ +About the PPP code: + +The PPP code is not our "own" code - we just copied it from pppd (http://ppp.samba.org/) and adapted it to lwIP. +Unfortunately, not many here know their way around it too well. Back in 2009, we took the effort to see which +version of pppd our code relates to and we're pretty much on 2.3.11 with some bugs from 2.4.x backported. + +Aside from simple code adaptions, there are some files that are different, however: +- chpms.c/.h are named chap_ms.c/.h in the original pppd 2.3.11 sources +- pap.c/.h are named upap.c/.h in the original pppd 2.3.11 sources +- randm.c is a random generator not included in the original pppd +- magic.c does not use the C library's random functions, but uses randm.c instead +- vj.c/.h is an implementation of the Van Jacobson header compression algorithm adapted to lwIP pbufs, + probably copied from one of the vjcompress.c files from pppd. +- ppp.c, ppp.h and ppp_impl.h contain the adaption from pppd to lwIP. This is the "OS"-dependent part like there + is an implementation for linux, xBSD etc. in the pppd sources. +- ppp_oe.c is Marc Boucher's implementation based on NetBSD's if_pppoe.c + +There is of course potential for bugs in it, but when analyzing of reporting bugs, it is strongly encouraged to +compare the code in question to pppd 2.3.11 (our basis) and newer versions (perhaps it's already fixed?) and to +share this knowledge with us when reporting a bug. + diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.c b/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.c new file mode 100644 index 0000000..40fdad1 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.c @@ -0,0 +1,652 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "vj.h" + +#include + +#if VJ_SUPPORT + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_char i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u_short tmp = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip_hdr *ip = (struct ip_hdr *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = IPH_HL(ip); + register struct tcp_hdr *oth; + register struct tcp_hdr *th; + register u_short deltaS, deltaA; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (IPH_PROTO(ip) != IP_PROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcp_hdr *)&((long *)ip)[hlen]; + if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src) + || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip_addr_cmp(&ip->src, &cs->cs_ip.src) + && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (TCPH_FLAGS(th) & TCP_URG) { + deltaS = ntohs(th->urgp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->urgp != oth->urgp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && + ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip))); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (TCPH_FLAGS(th) & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->chksum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)(changes | NEW_C); + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)changes; + } + *cp++ = (u_char)(deltaA >> 8); + *cp++ = (u_char)deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + IPH_PROTO_SET(ip, cs->cs_id); + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip_hdr *ip; + + ip = (struct ip_hdr *)nb->payload; + hlen = IPH_HL(ip) << 2; + if (IPH_PROTO(ip) >= MAX_SLOTS + || hlen + sizeof(struct tcp_hdr) > nb->len + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + IPH_PROTO(ip), hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; + comp->flags &=~ VJF_TOSS; + IPH_PROTO_SET(ip, IP_PROTO_TCP); + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = (u_short)hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcp_hdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = IPH_HL(&cs->cs_ip) << 2; + th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->chksum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + TCPH_SET_FLAG(th, TCP_PSH); + } else { + TCPH_UNSET_FLAG(th, TCP_PSH); + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->ackno) + i; + th->ackno = htonl(tmp); + tmp = ntohl(th->seqno) + i; + th->seqno = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + th->seqno = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + TCPH_SET_FLAG(th, TCP_URG); + DECODEU(th->urgp); + } else { + TCPH_UNSET_FLAG(th, TCP_URG); + } + if (changes & NEW_W) { + DECODES(th->wnd); + } + if (changes & NEW_A) { + DECODEL(th->ackno); + } + if (changes & NEW_S) { + DECODEL(th->seqno); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip._id); + } else { + IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1); + IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip))); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp)); +#else + IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen)); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + IPH_CHKSUM_SET(&cs->cs_ip, 0); + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp)); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* VJ_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.h b/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.h new file mode 100644 index 0000000..fad1213 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/ppp/vj.h @@ -0,0 +1,156 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef VJ_H +#define VJ_H + +#include "lwip/ip.h" +#include "lwip/tcp_impl.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ diff --git a/app/src/main/jni/badvpn/lwip/src/netif/slipif.c b/app/src/main/jni/badvpn/lwip/src/netif/slipif.c new file mode 100644 index 0000000..137ba89 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/src/netif/slipif.c @@ -0,0 +1,546 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + * Simon Goldschmidt + * + * Usage: This netif can be used in three ways: + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received. + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input(). + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" +#include "lwip/sys.h" + +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ + +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE +#define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u8_t state; + u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output(struct netif *netif, struct pbuf *p) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); + priv = netif->state; + + /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + /* normal byte - no need for escaping */ + sio_send(c, priv->sd); + break; + } + } + } + /* End with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} + +#if LWIP_IPV6 +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV6 */ + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) +{ + struct slipif_priv *priv; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + priv->state = SLIP_RECV_NORMAL; + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + return NULL; +} + +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + u8_t c; + struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + + while (1) { + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } + } +} +#endif /* SLIP_USE_RX_THREAD */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + u8_t sio_num; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output_v4; +#if LWIP_IPV6 + netif->output_ip6 = slipif_output_v6; +#endif /* LWIP_IPV6 */ + netif->mtu = SLIP_MAX_SIZE; + netif->flags |= NETIF_FLAG_POINTTOPOINT; + + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); + +#if SLIP_USE_RX_THREAD + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + u8_t c; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while(q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); + } +} +#endif /* SLIP_RX_FROM_ISR */ + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.c b/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.c new file mode 100644 index 0000000..d3a5d54 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.c @@ -0,0 +1,73 @@ +#include "test_mem.h" + +#include "lwip/mem.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !MEM_STATS +#error "This tests needs MEM-statistics enabled" +#endif +#if LWIP_DNS +#error "This test needs DNS turned off (as it mallocs on init)" +#endif + +/* Setups/teardown functions */ + +static void +mem_setup(void) +{ +} + +static void +mem_teardown(void) +{ +} + + +/* Test functions */ + +/** Call mem_malloc, mem_free and mem_trim and check stats */ +START_TEST(test_mem_one) +{ +#define SIZE1 16 +#define SIZE1_2 12 +#define SIZE2 16 + void *p1, *p2; + mem_size_t s1, s2; + LWIP_UNUSED_ARG(_i); + +#if LWIP_DNS + fail("This test needs DNS turned off (as it mallocs on init)"); +#endif + + fail_unless(lwip_stats.mem.used == 0); + + p1 = mem_malloc(SIZE1); + fail_unless(p1 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE1); + s1 = lwip_stats.mem.used; + + p2 = mem_malloc(SIZE2); + fail_unless(p2 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE2 + s1); + s2 = lwip_stats.mem.used; + + mem_trim(p1, SIZE1_2); + + mem_free(p2); + fail_unless(lwip_stats.mem.used <= s2 - SIZE2); + + mem_free(p1); + fail_unless(lwip_stats.mem.used == 0); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +mem_suite(void) +{ + TFun tests[] = { + test_mem_one + }; + return create_suite("MEM", tests, sizeof(tests)/sizeof(TFun), mem_setup, mem_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.h b/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.h new file mode 100644 index 0000000..13803ed --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/core/test_mem.h @@ -0,0 +1,8 @@ +#ifndef __TEST_MEM_H__ +#define __TEST_MEM_H__ + +#include "../lwip_check.h" + +Suite *mem_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.c b/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.c new file mode 100644 index 0000000..2911078 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.c @@ -0,0 +1,73 @@ +#include "test_pbuf.h" + +#include "lwip/pbuf.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS +#error "This tests needs MEM- and MEMP-statistics enabled" +#endif +#if LWIP_DNS +#error "This test needs DNS turned off (as it mallocs on init)" +#endif + +/* Setups/teardown functions */ + +static void +pbuf_setup(void) +{ +} + +static void +pbuf_teardown(void) +{ +} + + +/* Test functions */ + +/** Call pbuf_copy on a pbuf with zero length */ +START_TEST(test_pbuf_copy_zero_pbuf) +{ + struct pbuf *p1, *p2, *p3; + err_t err; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.mem.used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); + + p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM); + fail_unless(p1 != NULL); + fail_unless(p1->ref == 1); + + p2 = pbuf_alloc(PBUF_RAW, 2, PBUF_POOL); + fail_unless(p2 != NULL); + fail_unless(p2->ref == 1); + p2->len = p2->tot_len = 0; + + pbuf_cat(p1, p2); + fail_unless(p1->ref == 1); + fail_unless(p2->ref == 1); + + p3 = pbuf_alloc(PBUF_RAW, p1->tot_len, PBUF_POOL); + err = pbuf_copy(p3, p1); + fail_unless(err == ERR_VAL); + + pbuf_free(p1); + pbuf_free(p3); + fail_unless(lwip_stats.mem.used == 0); + + fail_unless(lwip_stats.mem.used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +pbuf_suite(void) +{ + TFun tests[] = { + test_pbuf_copy_zero_pbuf + }; + return create_suite("PBUF", tests, sizeof(tests)/sizeof(TFun), pbuf_setup, pbuf_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.h b/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.h new file mode 100644 index 0000000..b2715ad --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/core/test_pbuf.h @@ -0,0 +1,8 @@ +#ifndef __TEST_PBUF_H__ +#define __TEST_PBUF_H__ + +#include "../lwip_check.h" + +Suite *pbuf_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.c b/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.c new file mode 100644 index 0000000..4b40de8 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.c @@ -0,0 +1,916 @@ +#include "test_dhcp.h" + +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "netif/etharp.h" + +struct netif net_test; + +static const u8_t broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static const u8_t magic_cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + +static u8_t dhcp_offer[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */ + 0x00, 0x0F, 0xEE, 0x30, 0xAB, 0x22, /* From Remote host */ + 0x08, 0x00, /* Protocol: IP */ + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */ + 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */ + + 0x02, /* Type == Boot reply */ + 0x01, 0x06, /* Hw Ethernet, 6 bytes addrlen */ + 0x00, /* 0 hops */ + 0xAA, 0xAA, 0xAA, 0xAA, /* Transaction id, will be overwritten */ + 0x00, 0x00, /* 0 seconds elapsed */ + 0x00, 0x00, /* Flags (unicast) */ + 0x00, 0x00, 0x00, 0x00, /* Client ip */ + 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */ + 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server ip */ + 0x00, 0x00, 0x00, 0x00, /* relay agent */ + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MAC addr + padding */ + + /* Empty server name and boot file name */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x63, 0x82, 0x53, 0x63, /* Magic cookie */ + 0x35, 0x01, 0x02, /* Message type: Offer */ + 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Server identifier (IP) */ + 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */ + 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */ + 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Subnet mask */ + 0xff, /* End option */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */ +}; + +static u8_t dhcp_ack[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */ + 0x00, 0x0f, 0xEE, 0x30, 0xAB, 0x22, /* From remote host */ + 0x08, 0x00, /* Proto IP */ + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */ + 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */ + 0x02, /* Bootp reply */ + 0x01, 0x06, /* Hw type Eth, len 6 */ + 0x00, /* 0 hops */ + 0xAA, 0xAA, 0xAA, 0xAA, + 0x00, 0x00, /* 0 seconds elapsed */ + 0x00, 0x00, /* Flags (unicast) */ + 0x00, 0x00, 0x00, 0x00, /* Client IP */ + 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */ + 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server IP */ + 0x00, 0x00, 0x00, 0x00, /* Relay agent */ + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Macaddr + padding */ + + /* Empty server name and boot file name */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x63, 0x82, 0x53, 0x63, /* Magic cookie */ + 0x35, 0x01, 0x05, /* Dhcp message type ack */ + 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server identifier */ + 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */ + 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */ + 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Netmask */ + 0xff, /* End marker */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */ +}; + +static const u8_t arpreply[] = { + 0x00, 0x23, 0xC1, 0xDE, 0xD0, 0x0D, /* dst mac */ + 0x00, 0x32, 0x44, 0x20, 0x01, 0x02, /* src mac */ + 0x08, 0x06, /* proto arp */ + 0x00, 0x01, /* hw eth */ + 0x08, 0x00, /* proto ip */ + 0x06, /* hw addr len 6 */ + 0x04, /* proto addr len 4 */ + 0x00, 0x02, /* arp reply */ + 0x00, 0x32, 0x44, 0x20, 0x01, 0x02, /* sender mac */ + 0xc3, 0xaa, 0xbd, 0xc8, /* sender ip */ + 0x00, 0x23, 0xC1, 0xDE, 0xD0, 0x0D, /* target mac */ + 0x00, 0x00, 0x00, 0x00, /* target ip */ +}; + +static int txpacket; +static enum tcase { + TEST_LWIP_DHCP, + TEST_LWIP_DHCP_NAK, + TEST_LWIP_DHCP_RELAY, + TEST_LWIP_DHCP_NAK_NO_ENDMARKER, +} tcase; + +static int debug = 0; +static void setdebug(int a) {debug = a;} + +static int tick = 0; +static void tick_lwip(void) +{ + tick++; + if (tick % 5 == 0) { + dhcp_fine_tmr(); + } + if (tick % 600 == 0) { + dhcp_coarse_tmr(); + } +} + +static void send_pkt(struct netif *netif, const u8_t *data, u32_t len) +{ + struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + struct pbuf *q; + + if (debug) { + /* Dump data */ + u32_t i; + printf("RX data (len %d)", p->tot_len); + for (i = 0; i < len; i++) { + printf(" %02X", data[i]); + } + printf("\n"); + } + + fail_unless(p != NULL); + for(q = p; q != NULL; q = q->next) { + memcpy(q->payload, data, q->len); + data += q->len; + } + netif->input(p, netif); +} + +static err_t lwip_tx_func(struct netif *netif, struct pbuf *p); + +static err_t testif_init(struct netif *netif) +{ + netif->name[0] = 'c'; + netif->name[1] = 'h'; + netif->output = etharp_output; + netif->linkoutput = lwip_tx_func; + netif->mtu = 1500; + netif->hwaddr_len = 6; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + netif->hwaddr[0] = 0x00; + netif->hwaddr[1] = 0x23; + netif->hwaddr[2] = 0xC1; + netif->hwaddr[3] = 0xDE; + netif->hwaddr[4] = 0xD0; + netif->hwaddr[5] = 0x0D; + + return ERR_OK; +} + +static void dhcp_setup(void) +{ + txpacket = 0; +} + +static void dhcp_teardown(void) +{ +} + +static void check_pkt(struct pbuf *p, u32_t pos, const u8_t *mem, u32_t len) +{ + u8_t *data; + + fail_if((pos + len) > p->tot_len); + while (pos > p->len && p->next) { + pos -= p->len; + p = p->next; + } + fail_if(p == NULL); + fail_unless(pos + len <= p->len); /* All data we seek within same pbuf */ + + data = p->payload; + fail_if(memcmp(&data[pos], mem, len), "data at pos %d, len %d in packet %d did not match", pos, len, txpacket); +} + +static void check_pkt_fuzzy(struct pbuf *p, u32_t startpos, const u8_t *mem, u32_t len) +{ + int found; + u32_t i; + u8_t *data; + + fail_if((startpos + len) > p->tot_len); + while (startpos > p->len && p->next) { + startpos -= p->len; + p = p->next; + } + fail_if(p == NULL); + fail_unless(startpos + len <= p->len); /* All data we seek within same pbuf */ + + found = 0; + data = p->payload; + for (i = startpos; i <= (p->len - len); i++) { + if (memcmp(&data[i], mem, len) == 0) { + found = 1; + break; + } + } + fail_unless(found); +} + +static err_t lwip_tx_func(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &net_test); + txpacket++; + + if (debug) { + struct pbuf *pp = p; + /* Dump data */ + printf("TX data (pkt %d, len %d, tick %d)", txpacket, p->tot_len, tick); + do { + int i; + for (i = 0; i < pp->len; i++) { + printf(" %02X", ((u8_t *) pp->payload)[i]); + } + if (pp->next) { + pp = pp->next; + } + } while (pp->next); + printf("\n"); + } + + switch (tcase) { + case TEST_LWIP_DHCP: + switch (txpacket) { + case 1: + case 2: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + if (txpacket == 1) { + u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; + check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); + } else if (txpacket == 2) { + u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + u8_t requested_ipaddr[] = { 0x32, 0x04, 0xc3, 0xaa, 0xbd, 0xc8 }; /* Ask for offered IP */ + + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + } + break; + } + case 3: + case 4: + case 5: + { + const u8_t arpproto[] = { 0x08, 0x06 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ + break; + } + } + break; + + case TEST_LWIP_DHCP_NAK: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8_t dhcp_nak_opt[] = { 0x35, 0x01, 0x04 }; + const u8_t requested_ipaddr[] = { 0x32, 0x04, 0xc3, 0xaa, 0xbd, 0xc8 }; /* offered IP */ + + fail_unless(txpacket == 4); + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + check_pkt_fuzzy(p, 282, dhcp_nak_opt, sizeof(dhcp_nak_opt)); /* NAK the ack */ + + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + break; + } + + case TEST_LWIP_DHCP_RELAY: + switch (txpacket) { + case 1: + case 2: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + if (txpacket == 1) { + u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; + check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); + } else if (txpacket == 2) { + u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + u8_t requested_ipaddr[] = { 0x32, 0x04, 0x4f, 0x8a, 0x33, 0x05 }; /* Ask for offered IP */ + + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + } + break; + } + case 3: + case 4: + case 5: + case 6: + { + const u8_t arpproto[] = { 0x08, 0x06 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ + break; + } + case 7: + { + const u8_t fake_arp[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab }; + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + + check_pkt(p, 0, fake_arp, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + break; + } + } + break; + + default: + break; + } + + return ERR_OK; +} + +/* + * Test basic happy flow DHCP session. + * Validate that xid is checked. + */ +START_TEST(test_dhcp) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + int i; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1, "TX %d packets, expected 1", txpacket); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(txpacket == 2, "TX %d packets, expected 2", txpacket); /* DHCP request sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_ack[46], &xid, 4); + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 2, "TX %d packets, still expected 2", txpacket); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + for (i = 0; i < 20; i++) { + tick_lwip(); + } + fail_unless(txpacket == 4, "TX %d packets, expected 4", txpacket); /* ARP requests sent */ + + /* Interface up */ + fail_unless(netif_is_up(&net_test)); + + /* Now it should have taken the IP */ + IP4_ADDR(&addr, 195, 170, 189, 200); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 195, 170, 189, 171); + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + netif_remove(&net_test); +} +END_TEST + +/* + * Test that IP address is not taken and NAK is sent if someone + * replies to ARP requests for the offered address. + */ +START_TEST(test_dhcp_nak) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(txpacket == 2); /* DHCP request sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_ack[46], &xid, 4); + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 2); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 3); /* ARP request sent */ + + tcase = TEST_LWIP_DHCP_NAK; /* Switch testcase */ + + /* Send arp reply, mark offered IP as taken */ + send_pkt(&net_test, arpreply, sizeof(arpreply)); + + fail_unless(txpacket == 4); /* DHCP nak sent */ + + netif_remove(&net_test); +} +END_TEST + +/* + * Test case based on captured data where + * replies are sent from a different IP than the + * one the client unicasted to. + */ +START_TEST(test_dhcp_relayed) +{ + const u8_t relay_offer[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, + 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfd, 0x53, 0x00, 0x00, 0x40, 0x11, + 0x78, 0x46, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x51, 0x35, + 0xb6, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x49, 0x35, 0x01, 0x02, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff + }; + + const u8_t relay_ack1[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x22, + 0x93, 0x5a, 0xf7, 0x60, 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfd, 0x55, 0x00, 0x00, 0x40, 0x11, + 0x78, 0x44, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x51, 0x35, + 0xb6, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x49, 0x35, 0x01, 0x05, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff + }; + + const u8_t relay_ack2[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, + 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfa, 0x18, 0x00, 0x00, 0x40, 0x11, + 0x7b, 0x81, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x49, 0x8b, + 0x6e, 0xab, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x8a, + 0x33, 0x05, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x60, 0x35, 0x01, 0x05, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff }; + + const u8_t arp_resp[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* DEST */ + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, /* SRC */ + 0x08, 0x06, /* Type: ARP */ + 0x00, 0x01, /* HW: Ethernet */ + 0x08, 0x00, /* PROTO: IP */ + 0x06, /* HW size */ + 0x04, /* PROTO size */ + 0x00, 0x02, /* OPCODE: Reply */ + + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, /* Target MAC */ + 0x4f, 0x8a, 0x32, 0x01, /* Target IP */ + + 0x00, 0x23, 0xc1, 0x00, 0x06, 0x50, /* src mac */ + 0x4f, 0x8a, 0x33, 0x05, /* src ip */ + + /* Padding follows.. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + int i; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP_RELAY; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&relay_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, relay_offer, sizeof(relay_offer)); + + /* request sent? */ + fail_unless(txpacket == 2, "txpkt = %d, should be 2", txpacket); + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&relay_ack1[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, relay_ack1, sizeof(relay_ack1)); + + for (i = 0; i < 25; i++) { + tick_lwip(); + } + fail_unless(txpacket == 4, "txpkt should be 5, is %d", txpacket); /* ARP requests sent */ + + /* Interface up */ + fail_unless(netif_is_up(&net_test)); + + /* Now it should have taken the IP */ + IP4_ADDR(&addr, 79, 138, 51, 5); + IP4_ADDR(&netmask, 255, 255, 254, 0); + IP4_ADDR(&gw, 79, 138, 50, 1); + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 4, "txpacket = %d", txpacket); + + for (i = 0; i < 108000 - 25; i++) { + tick_lwip(); + } + + fail_unless(netif_is_up(&net_test)); + fail_unless(txpacket == 6, "txpacket = %d", txpacket); + + /* We need to send arp response here.. */ + + send_pkt(&net_test, arp_resp, sizeof(arp_resp)); + + fail_unless(txpacket == 7, "txpacket = %d", txpacket); + fail_unless(netif_is_up(&net_test)); + + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&relay_ack2[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, relay_ack2, sizeof(relay_ack2)); + + for (i = 0; i < 100000; i++) { + tick_lwip(); + } + + fail_unless(txpacket == 7, "txpacket = %d", txpacket); + + netif_remove(&net_test); + +} +END_TEST + +START_TEST(test_dhcp_nak_no_endmarker) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + + u8_t dhcp_nack_no_endmarker[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0x75, + 0xd0, 0x26, 0xd0, 0x0d, 0x08, 0x00, 0x45, 0x00, + 0x01, 0x15, 0x38, 0x86, 0x00, 0x00, 0xff, 0x11, + 0xc0, 0xa8, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x43, 0x00, 0x44, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x7a, 0xcb, + 0xba, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x35, 0x01, 0x06, 0x36, 0x04, 0xc0, + 0xa8, 0x01, 0x01, 0x31, 0xef, 0xad, 0x72, 0x31, + 0x43, 0x4e, 0x44, 0x30, 0x32, 0x35, 0x30, 0x43, + 0x52, 0x47, 0x44, 0x38, 0x35, 0x36, 0x3c, 0x08, + 0x4d, 0x53, 0x46, 0x54, 0x20, 0x35, 0x2e, 0x30, + 0x37, 0x0d, 0x01, 0x0f, 0x03, 0x06, 0x2c, 0x2e, + 0x2f, 0x1f, 0x21, 0x79, 0xf9, 0x2b, 0xfc, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x71, + 0xf3, 0x5b, 0xe2, 0x71, 0x2e, 0x01, 0x08, 0x03, + 0x04, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0xeb, 0x1e, + 0x44, 0xec, 0xeb, 0x1e, 0x30, 0x37, 0x0c, 0x01, + 0x0f, 0x03, 0x06, 0x2c, 0x2e, 0x2f, 0x1f, 0x21, + 0x79, 0xf9, 0x2b, 0xff, 0x25, 0xc0, 0x09, 0xd6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP_NAK_NO_ENDMARKER; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(net_test.dhcp->state == DHCP_REQUESTING); + + fail_unless(txpacket == 2); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_nack_no_endmarker[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_nack_no_endmarker, sizeof(dhcp_nack_no_endmarker)); + + /* NAK should put us in another state for a while, no other way detecting it */ + fail_unless(net_test.dhcp->state != DHCP_REQUESTING); + + netif_remove(&net_test); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +dhcp_suite(void) +{ + TFun tests[] = { + test_dhcp, + test_dhcp_nak, + test_dhcp_relayed, + test_dhcp_nak_no_endmarker + }; + return create_suite("DHCP", tests, sizeof(tests)/sizeof(TFun), dhcp_setup, dhcp_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.h b/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.h new file mode 100644 index 0000000..aff44b7 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/dhcp/test_dhcp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_DHCP_H__ +#define __TEST_DHCP_H__ + +#include "../lwip_check.h" + +Suite* dhcp_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.c b/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.c new file mode 100644 index 0000000..cbbc950 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.c @@ -0,0 +1,262 @@ +#include "test_etharp.h" + +#include "lwip/udp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS +#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled" +#endif +#if !ETHARP_SUPPORT_STATIC_ENTRIES +#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled" +#endif + +static struct netif test_netif; +static ip_addr_t test_ipaddr, test_netmask, test_gw; +struct eth_addr test_ethaddr = {1,1,1,1,1,1}; +struct eth_addr test_ethaddr2 = {1,1,1,1,1,2}; +struct eth_addr test_ethaddr3 = {1,1,1,1,1,3}; +struct eth_addr test_ethaddr4 = {1,1,1,1,1,4}; +static int linkoutput_ctr; + +/* Helper functions */ +static void +etharp_remove_all(void) +{ + int i; + /* call etharp_tmr often enough to have all entries cleaned */ + for(i = 0; i < 0xff; i++) { + etharp_tmr(); + } +} + +static err_t +default_netif_linkoutput(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &test_netif); + fail_unless(p != NULL); + linkoutput_ctr++; + return ERR_OK; +} + +static err_t +default_netif_init(struct netif *netif) +{ + fail_unless(netif != NULL); + netif->linkoutput = default_netif_linkoutput; + netif->output = etharp_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + netif->hwaddr_len = ETHARP_HWADDR_LEN; + return ERR_OK; +} + +static void +default_netif_add(void) +{ + IP4_ADDR(&test_gw, 192,168,0,1); + IP4_ADDR(&test_ipaddr, 192,168,0,1); + IP4_ADDR(&test_netmask, 255,255,0,0); + + fail_unless(netif_default == NULL); + netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask, + &test_gw, NULL, default_netif_init, NULL)); + netif_set_up(&test_netif); +} + +static void +default_netif_remove(void) +{ + fail_unless(netif_default == &test_netif); + netif_remove(&test_netif); +} + +static void +create_arp_response(ip_addr_t *adr) +{ + int k; + struct eth_hdr *ethhdr; + struct etharp_hdr *etharphdr; + struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM); + if(p == NULL) { + FAIL_RET(); + } + ethhdr = (struct eth_hdr*)p->payload; + etharphdr = (struct etharp_hdr*)(ethhdr + 1); + + ethhdr->dest = test_ethaddr; + ethhdr->src = test_ethaddr2; + ethhdr->type = htons(ETHTYPE_ARP); + + etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1); + etharphdr->proto = htons(ETHTYPE_IP); + etharphdr->hwlen = ETHARP_HWADDR_LEN; + etharphdr->protolen = sizeof(ip_addr_t); + etharphdr->opcode = htons(ARP_REPLY); + + SMEMCPY(ðarphdr->sipaddr, adr, sizeof(ip_addr_t)); + SMEMCPY(ðarphdr->dipaddr, &test_ipaddr, sizeof(ip_addr_t)); + + k = 6; + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k]; + etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k]; + /* Write the Ethernet MAC-Addresses */ + ethhdr->dest.addr[k] = test_ethaddr.addr[k]; + ethhdr->src.addr[k] = test_ethaddr2.addr[k]; + } + + ethernet_input(p, &test_netif); +} + +/* Setups/teardown functions */ + +static void +etharp_setup(void) +{ + etharp_remove_all(); + default_netif_add(); +} + +static void +etharp_teardown(void) +{ + etharp_remove_all(); + default_netif_remove(); +} + + +/* Test functions */ + +START_TEST(test_etharp_table) +{ +#if ETHARP_SUPPORT_STATIC_ENTRIES + err_t err; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + s8_t idx; + ip_addr_t *unused_ipaddr; + struct eth_addr *unused_ethaddr; + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + if (netif_default != &test_netif) { + fail("This test needs a default netif"); + } + + linkoutput_ctr = 0; + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + ip_addr_t adrs[ARP_TABLE_SIZE + 2]; + int i; + for(i = 0; i < ARP_TABLE_SIZE + 2; i++) { + IP4_ADDR(&adrs[i], 192,168,0,i+2); + } + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == i); + etharp_tmr(); + } + } + linkoutput_ctr = 0; +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create one static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + fail_unless(linkoutput_ctr == 0); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + linkoutput_ctr = 0; + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + if (i < ARP_TABLE_SIZE - 1) { + fail_unless(idx == i+1); + } else { + /* the last entry must not overwrite the static entry! */ + fail_unless(idx == 1); + } + etharp_tmr(); + } + } +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create a second static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 2); + /* and remove it again */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + /* check that static entries don't time out */ + etharp_remove_all(); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* remove the first static entry */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + udp_remove(pcb); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +etharp_suite(void) +{ + TFun tests[] = { + test_etharp_table + }; + return create_suite("ETHARP", tests, sizeof(tests)/sizeof(TFun), etharp_setup, etharp_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.h b/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.h new file mode 100644 index 0000000..96e00c3 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/etharp/test_etharp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_ETHARP_H__ +#define __TEST_ETHARP_H__ + +#include "../lwip_check.h" + +Suite* etharp_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/lwip_check.h b/app/src/main/jni/badvpn/lwip/test/unit/lwip_check.h new file mode 100644 index 0000000..e27f55a --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/lwip_check.h @@ -0,0 +1,37 @@ +#ifndef __LWIP_CHECK_H__ +#define __LWIP_CHECK_H__ + +/* Common header file for lwIP unit tests using the check framework */ + +#include +#include +#include + +#define FAIL_RET() do { fail(); return; } while(0) +#define EXPECT(x) fail_unless(x) +#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0) +#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0) +#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL) + +/** typedef for a function returning a test suite */ +typedef Suite* (suite_getter_fn)(void); + +/** Create a test suite */ +static Suite* create_suite(const char* name, TFun *tests, size_t num_tests, SFun setup, SFun teardown) +{ + size_t i; + Suite *s = suite_create(name); + + for(i = 0; i < num_tests; i++) { + /* Core test case */ + TCase *tc_core = tcase_create("Core"); + if ((setup != NULL) || (teardown != NULL)) { + tcase_add_checked_fixture(tc_core, setup, teardown); + } + tcase_add_test(tc_core, tests[i]); + suite_add_tcase(s, tc_core); + } + return s; +} + +#endif /* __LWIP_CHECK_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/test/unit/lwip_unittests.c b/app/src/main/jni/badvpn/lwip/test/unit/lwip_unittests.c new file mode 100644 index 0000000..41d1f36 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/lwip_unittests.c @@ -0,0 +1,49 @@ +#include "lwip_check.h" + +#include "udp/test_udp.h" +#include "tcp/test_tcp.h" +#include "tcp/test_tcp_oos.h" +#include "core/test_mem.h" +#include "core/test_pbuf.h" +#include "etharp/test_etharp.h" +#include "dhcp/test_dhcp.h" + +#include "lwip/init.h" + + +int main() +{ + int number_failed; + SRunner *sr; + size_t i; + suite_getter_fn* suites[] = { + udp_suite, + tcp_suite, + tcp_oos_suite, + mem_suite, + pbuf_suite, + etharp_suite, + dhcp_suite + }; + size_t num = sizeof(suites)/sizeof(void*); + LWIP_ASSERT("No suites defined", num > 0); + + lwip_init(); + + sr = srunner_create((suites[0])()); + for(i = 1; i < num; i++) { + srunner_add_suite(sr, ((suite_getter_fn*)suites[i])()); + } + +#ifdef LWIP_UNITTESTS_NOFORK + srunner_set_fork_status(sr, CK_NOFORK); +#endif +#ifdef LWIP_UNITTESTS_FORK + srunner_set_fork_status(sr, CK_FORK); +#endif + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/lwipopts.h b/app/src/main/jni/badvpn/lwip/test/unit/lwipopts.h new file mode 100644 index 0000000..0059515 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/lwipopts.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 + +/* Enable DHCP to test it, disable UDP checksum to easier inject packets */ +#define LWIP_DHCP 1 + +/* Minimal changes to opt.h required for tcp unit tests: */ +#define MEM_SIZE 16000 +#define TCP_SND_QUEUELEN 40 +#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN +#define TCP_SND_BUF (12 * TCP_MSS) +#define TCP_WND (10 * TCP_MSS) + +/* Minimal changes to opt.h required for etharp unit tests: */ +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#endif /* __LWIPOPTS_H__ */ diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.c b/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.c new file mode 100644 index 0000000..ff503db --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.c @@ -0,0 +1,303 @@ +#include "tcp_helper.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "lwip/pbuf.h" +#include "lwip/inet_chksum.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif + +/** Remove all pcbs on the given list. */ +static void +tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + +/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */ +void +tcp_remove_all(void) +{ + tcp_remove(tcp_listen_pcbs.pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB_LISTEN].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_SEG].used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); +} + +/** Create a TCP segment usable for passing to tcp_input */ +static struct pbuf* +tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd) +{ + struct pbuf *p, *q; + struct ip_hdr* iphdr; + struct tcp_hdr* tcphdr; + u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len); + + p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL); + EXPECT_RETNULL(p != NULL); + /* first pbuf must be big enough to hold the headers */ + EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + if (data_len > 0) { + /* first pbuf must be big enough to hold at least 1 data byte, too */ + EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + } + + for(q = p; q != NULL; q = q->next) { + memset(q->payload, 0, q->len); + } + + iphdr = p->payload; + /* fill IP header */ + iphdr->dest.addr = dst_ip->addr; + iphdr->src.addr = src_ip->addr; + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* let p point to TCP header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcphdr = p->payload; + tcphdr->src = htons(src_port); + tcphdr->dest = htons(dst_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4); + TCPH_FLAGS_SET(tcphdr, headerflags); + tcphdr->wnd = htons(wnd); + + if (data_len > 0) { + /* let p point to TCP data */ + pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr)); + /* copy data */ + pbuf_take(p, data, data_len); + /* let p point to TCP header again */ + pbuf_header(p, sizeof(struct tcp_hdr)); + } + + /* calculate checksum */ + + tcphdr->chksum = inet_chksum_pseudo(p, + IP_PROTO_TCP, p->tot_len, src_ip, dst_ip); + + pbuf_header(p, sizeof(struct ip_hdr)); + + return p; +} + +/** Create a TCP segment usable for passing to tcp_input */ +struct pbuf* +tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags) +{ + return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data, + data_len, seqno, ackno, headerflags, TCP_WND); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + */ +struct pbuf* +tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset, + u32_t ackno_offset, u8_t headerflags) +{ + return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port, + data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + * - TCP window can be adjusted + */ +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd) +{ + return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port, + data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd); +} + +/** Safely bring a tcp_pcb into the requested state */ +void +tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port) +{ + /* @todo: are these all states? */ + /* @todo: remove from previous list */ + pcb->state = state; + if (state == ESTABLISHED) { + TCP_REG(&tcp_active_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else if(state == LISTEN) { + TCP_REG(&tcp_listen_pcbs.pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + } else if(state == TIME_WAIT) { + TCP_REG(&tcp_tw_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else { + fail(); + } +} + +void +test_tcp_counters_err(void* arg, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RET(arg != NULL); + counters->err_calls++; + counters->last_err = err; +} + +static void +test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p) +{ + struct pbuf* q; + u32_t i, received; + if(counters->expected_data == NULL) { + /* no data to compare */ + return; + } + EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len); + received = counters->recved_bytes; + for(q = p; q != NULL; q = q->next) { + char *data = q->payload; + for(i = 0; i < q->len; i++) { + EXPECT_RET(data[i] == counters->expected_data[received]); + received++; + } + } + EXPECT(received == counters->recved_bytes + p->tot_len); +} + +err_t +test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RETX(arg != NULL, ERR_OK); + EXPECT_RETX(pcb != NULL, ERR_OK); + EXPECT_RETX(err == ERR_OK, ERR_OK); + + if (p != NULL) { + if (counters->close_calls == 0) { + counters->recv_calls++; + test_tcp_counters_check_rxdata(counters, p); + counters->recved_bytes += p->tot_len; + } else { + counters->recv_calls_after_close++; + counters->recved_bytes_after_close += p->tot_len; + } + pbuf_free(p); + } else { + counters->close_calls++; + } + EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0); + return ERR_OK; +} + +/** Allocate a pcb and set up the test_tcp_counters_* callbacks */ +struct tcp_pcb* +test_tcp_new_counters_pcb(struct test_tcp_counters* counters) +{ + struct tcp_pcb* pcb = tcp_new(); + if (pcb != NULL) { + /* set up args and callbacks */ + tcp_arg(pcb, counters); + tcp_recv(pcb, test_tcp_counters_recv); + tcp_err(pcb, test_tcp_counters_err); + pcb->snd_wnd = TCP_WND; + pcb->snd_wnd_max = TCP_WND; + } + return pcb; +} + +/** Calls tcp_input() after adjusting current_iphdr_dest */ +void test_tcp_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + /* these lines are a hack, don't use them as an example :-) */ + ip_addr_copy(*ipX_current_dest_addr(), iphdr->dest); + ip_addr_copy(*ipX_current_src_addr(), iphdr->src); + ip_current_netif() = inp; + ip_current_header() = iphdr; + + /* since adding IPv6, p->payload must point to tcp header, not ip header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcp_input(p, inp); + + ipX_current_dest_addr()->addr = 0; + ipX_current_src_addr()->addr = 0; + ip_current_netif() = NULL; + ip_current_header() = NULL; +} + +static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state; + LWIP_UNUSED_ARG(ipaddr); + if (txcounters != NULL) + { + txcounters->num_tx_calls++; + txcounters->num_tx_bytes += p->tot_len; + if (txcounters->copy_tx_packets) { + struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + err_t err; + EXPECT(p_copy != NULL); + err = pbuf_copy(p_copy, p); + EXPECT(err == ERR_OK); + if (txcounters->tx_packets == NULL) { + txcounters->tx_packets = p_copy; + } else { + pbuf_cat(txcounters->tx_packets, p_copy); + } + } + } + return ERR_OK; +} + +void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters, + ip_addr_t *ip_addr, ip_addr_t *netmask) +{ + struct netif *n; + memset(netif, 0, sizeof(struct netif)); + if (txcounters != NULL) { + memset(txcounters, 0, sizeof(struct test_tcp_txcounters)); + netif->state = txcounters; + } + netif->output = test_tcp_netif_output; + netif->flags |= NETIF_FLAG_UP; + ip_addr_copy(netif->netmask, *netmask); + ip_addr_copy(netif->ip_addr, *ip_addr); + for (n = netif_list; n != NULL; n = n->next) { + if (n == netif) { + return; + } + } + netif->next = NULL; + netif_list = netif; +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.h b/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.h new file mode 100644 index 0000000..4a72c93 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/tcp_helper.h @@ -0,0 +1,52 @@ +#ifndef __TCP_HELPER_H__ +#define __TCP_HELPER_H__ + +#include "../lwip_check.h" +#include "lwip/arch.h" +#include "lwip/tcp.h" +#include "lwip/netif.h" + +/* counters used for test_tcp_counters_* callback functions */ +struct test_tcp_counters { + u32_t recv_calls; + u32_t recved_bytes; + u32_t recv_calls_after_close; + u32_t recved_bytes_after_close; + u32_t close_calls; + u32_t err_calls; + err_t last_err; + char* expected_data; + u32_t expected_data_len; +}; + +struct test_tcp_txcounters { + u32_t num_tx_calls; + u32_t num_tx_bytes; + u8_t copy_tx_packets; + struct pbuf *tx_packets; +}; + +/* Helper functions */ +void tcp_remove_all(void); + +struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags); +struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags); +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd); +void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port); +void test_tcp_counters_err(void* arg, err_t err); +err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); + +struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters); + +void test_tcp_input(struct pbuf *p, struct netif *inp); + +void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters, + ip_addr_t *ip_addr, ip_addr_t *netmask); + + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.c b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.c new file mode 100644 index 0000000..8172098 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.c @@ -0,0 +1,671 @@ +#include "test_tcp.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */ +#endif + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif +#if TCP_SND_BUF <= TCP_WND +#error "This tests needs TCP_SND_BUF to be > TCP_WND" +#endif + +static u8_t test_tcp_timer; + +/* our own version of tcp_tmr so we can reset fast/slow timer state */ +static void +test_tcp_tmr(void) +{ + tcp_fasttmr(); + if (++test_tcp_timer & 1) { + tcp_slowtmr(); + } +} + +/* Setups/teardown functions */ + +static void +tcp_setup(void) +{ + /* reset iss to default (6510) */ + tcp_ticks = 0; + tcp_ticks = 0 - (tcp_next_iss() - 6510); + tcp_next_iss(); + tcp_ticks = 0; + + test_tcp_timer = 0; + tcp_remove_all(); +} + +static void +tcp_teardown(void) +{ + tcp_remove_all(); + netif_list = NULL; + netif_default = NULL; +} + + +/* Test functions */ + +/** Call tcp_new() and tcp_abort() and test memp stats */ +START_TEST(test_tcp_new_abort) +{ + struct tcp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + + pcb = tcp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + } +} +END_TEST + +/** Create an ESTABLISHED pcb and check if receive callback is called */ +START_TEST(test_tcp_recv_inseq) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + char data[] = {1, 2, 3, 4}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + struct test_tcp_txcounters txcounters; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create a segment */ + p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); + EXPECT(p != NULL); + if (p != NULL) { + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. + * At the end, send more data. */ +START_TEST(test_tcp_fast_retx_recover) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + char data1[] = { 1, 2, 3, 4}; + char data2[] = { 5, 6, 7, 8}; + char data3[] = { 9, 10, 11, 12}; + char data4[] = {13, 14, 15, 16}; + char data5[] = {17, 18, 19, 20}; + char data6[] = {21, 22, 23, 24}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = pcb->snd_wnd; + + /* send data1 */ + err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(txcounters.num_tx_calls == 1); + EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); + memset(&txcounters, 0, sizeof(txcounters)); + /* "recv" ACK for data1 */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->unacked == NULL); + /* send data2 */ + err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(txcounters.num_tx_calls == 1); + EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); + memset(&txcounters, 0, sizeof(txcounters)); + /* duplicate ACK for data1 (data2 is lost) */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->dupacks == 1); + /* send data3 */ + err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* nagle enabled, no tx calls */ + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + /* 2nd duplicate ACK for data1 (data2 and data3 are lost) */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->dupacks == 2); + /* queue data4, don't send it (unsent-oversize is != 0) */ + err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + /* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + /*EXPECT_RET(txcounters.num_tx_calls == 1);*/ + EXPECT_RET(pcb->dupacks == 3); + memset(&txcounters, 0, sizeof(txcounters)); + /* TODO: check expected data?*/ + + /* send data5, not output yet */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + /*err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK);*/ + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + { + int i = 0; + do + { + err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY); + i++; + }while(err == ERR_OK); + EXPECT_RET(err != ERR_OK); + } + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /*EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0);*/ + memset(&txcounters, 0, sizeof(txcounters)); + + /* send even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + + /* send ACKs for data2 and data3 */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + /*EXPECT_RET(txcounters.num_tx_calls == 0);*/ + + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + +#if 0 + /* create expected segment */ + p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); + EXPECT_RET(p != NULL); + if (p != NULL) { + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT_RET(counters.close_calls == 0); + EXPECT_RET(counters.recv_calls == 1); + EXPECT_RET(counters.recved_bytes == data_len); + EXPECT_RET(counters.err_calls == 0); + } +#endif + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +static u8_t tx_data[TCP_WND*2]; + +static void +check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected) +{ + struct tcp_seg *s = segs; + int i; + for (i = 0; i < num_expected; i++, s = s->next) { + EXPECT_RET(s != NULL); + EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i])); + } + EXPECT(s == NULL); +} + +/** Send data with sequence numbers that wrap around the u32_t range. + * Then, provoke fast retransmission by duplicate ACKs and check that all + * segment lists are still properly sorted. */ +START_TEST(test_tcp_fast_rexmit_wraparound) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)i; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + tcp_ticks = SEQNO1 - ISS; + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = 2*TCP_MSS; + + /* send 6 mss-sized segments */ + for (i = 0; i < 6; i++) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + sent_total += TCP_MSS; + } + check_seqnos(pcb->unsent, 6, seqnos); + EXPECT(pcb->unacked == NULL); + err = tcp_output(pcb); + EXPECT(txcounters.num_tx_calls == 2); + EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); + memset(&txcounters, 0, sizeof(txcounters)); + + check_seqnos(pcb->unacked, 2, seqnos); + check_seqnos(pcb->unsent, 4, &seqnos[2]); + + /* ACK the first segment */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK); + test_tcp_input(p, &netif); + /* ensure this didn't trigger a retransmission */ + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + check_seqnos(pcb->unacked, 2, &seqnos[1]); + check_seqnos(pcb->unsent, 3, &seqnos[3]); + + /* 3 dupacks */ + EXPECT(pcb->dupacks == 0); + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(pcb->dupacks == 1); + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(pcb->dupacks == 2); + /* 3rd dupack -> fast rexmit */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(pcb->dupacks == 3); + EXPECT(txcounters.num_tx_calls == 4); + memset(&txcounters, 0, sizeof(txcounters)); + EXPECT(pcb->unsent == NULL); + check_seqnos(pcb->unacked, 5, &seqnos[1]); + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Send data with sequence numbers that wrap around the u32_t range. + * Then, provoke RTO retransmission and check that all + * segment lists are still properly sorted. */ +START_TEST(test_tcp_rto_rexmit_wraparound) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)i; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + tcp_ticks = 0; + tcp_ticks = 0 - tcp_next_iss(); + tcp_ticks = SEQNO1 - tcp_next_iss(); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = 2*TCP_MSS; + + /* send 6 mss-sized segments */ + for (i = 0; i < 6; i++) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + sent_total += TCP_MSS; + } + check_seqnos(pcb->unsent, 6, seqnos); + EXPECT(pcb->unacked == NULL); + err = tcp_output(pcb); + EXPECT(txcounters.num_tx_calls == 2); + EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); + memset(&txcounters, 0, sizeof(txcounters)); + + check_seqnos(pcb->unacked, 2, seqnos); + check_seqnos(pcb->unsent, 4, &seqnos[2]); + + /* call the tcp timer some times */ + for (i = 0; i < 10; i++) { + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 0); + } + /* 11th call to tcp_tmr: RTO rexmit fires */ + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 1); + check_seqnos(pcb->unacked, 1, seqnos); + check_seqnos(pcb->unsent, 5, &seqnos[1]); + + /* fake greater cwnd */ + pcb->cwnd = pcb->snd_wnd; + /* send more data */ + err = tcp_output(pcb); + EXPECT(err == ERR_OK); + /* check queues are sorted */ + EXPECT(pcb->unsent == NULL); + check_seqnos(pcb->unacked, 6, seqnos); + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. + * At the end, send more data. */ +static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; + u16_t sent_total, i; + u8_t expected = 0xFE; + + for (i = 0; i < sizeof(tx_data); i++) { + u8_t d = (u8_t)i; + if (d == 0xFE) { + d = 0xF0; + } + tx_data[i] = d; + } + if (zero_window_probe_from_unsent) { + tx_data[TCP_WND] = expected; + } else { + tx_data[0] = expected; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + memset(&txcounters, 0, sizeof(txcounters)); + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = pcb->snd_wnd; + + /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */ + sent_total = 0; + if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) { + u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS; + err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + sent_total += initial_data_len; + } + for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + } + EXPECT(sent_total == (TCP_WND - TCP_MSS)); + + /* now ACK the packet before the first */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + /* ensure this didn't trigger a retransmission */ + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + + EXPECT(pcb->persist_backoff == 0); + /* send the last packet, now a complete window has been sent */ + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + sent_total += TCP_MSS; + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + EXPECT(pcb->persist_backoff == 0); + + if (zero_window_probe_from_unsent) { + /* ACK all data but close the TX window */ + p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0); + test_tcp_input(p, &netif); + /* ensure this didn't trigger any transmission */ + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + EXPECT(pcb->persist_backoff == 1); + } + + /* send one byte more (out of window) -> persist timer starts */ + err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + if (!zero_window_probe_from_unsent) { + /* no persist timer unless a zero window announcement has been received */ + EXPECT(pcb->persist_backoff == 0); + } else { + EXPECT(pcb->persist_backoff == 1); + + /* call tcp_timer some more times to let persist timer count up */ + for (i = 0; i < 4; i++) { + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + } + + /* this should trigger the zero-window-probe */ + txcounters.copy_tx_packets = 1; + test_tcp_tmr(); + txcounters.copy_tx_packets = 0; + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == 1 + 40U); + EXPECT(txcounters.tx_packets != NULL); + if (txcounters.tx_packets != NULL) { + u8_t sent; + u16_t ret; + ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U); + EXPECT(ret == 1); + EXPECT(sent == expected); + } + if (txcounters.tx_packets != NULL) { + pbuf_free(txcounters.tx_packets); + txcounters.tx_packets = NULL; + } + } + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} + +START_TEST(test_tcp_tx_full_window_lost_from_unsent) +{ + LWIP_UNUSED_ARG(_i); + test_tcp_tx_full_window_lost(1); +} +END_TEST + +START_TEST(test_tcp_tx_full_window_lost_from_unacked) +{ + LWIP_UNUSED_ARG(_i); + test_tcp_tx_full_window_lost(0); +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +tcp_suite(void) +{ + TFun tests[] = { + test_tcp_new_abort, + test_tcp_recv_inseq, + test_tcp_fast_retx_recover, + test_tcp_fast_rexmit_wraparound, + test_tcp_rto_rexmit_wraparound, + test_tcp_tx_full_window_lost_from_unacked, + test_tcp_tx_full_window_lost_from_unsent + }; + return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.h b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.h new file mode 100644 index 0000000..f1c4a46 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_H__ +#define __TEST_TCP_H__ + +#include "../lwip_check.h" + +Suite *tcp_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.c b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.c new file mode 100644 index 0000000..612c468 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.c @@ -0,0 +1,958 @@ +#include "test_tcp_oos.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif +#if !TCP_QUEUE_OOSEQ +#error "This tests needs TCP_QUEUE_OOSEQ enabled" +#endif + +/** CHECK_SEGMENTS_ON_OOSEQ: + * 1: check count, seqno and len of segments on pcb->ooseq (strict) + * 0: only check that bytes are received in correct order (less strict) */ +#define CHECK_SEGMENTS_ON_OOSEQ 1 + +#if CHECK_SEGMENTS_ON_OOSEQ +#define EXPECT_OOSEQ(x) EXPECT(x) +#else +#define EXPECT_OOSEQ(x) +#endif + +/* helper functions */ + +/** Get the numbers of segments on the ooseq list */ +static int tcp_oos_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num++; + seg = seg->next; + } + return num; +} + +/** Get the numbers of pbufs on the ooseq list */ +static int tcp_oos_pbuf_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num += pbuf_clen(seg->p); + seg = seg->next; + } + return num; +} + +/** Get the seqno of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return seqno of the segment + */ +static u32_t +tcp_oos_seg_seqno(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return seg->tcphdr->seqno; + } + num++; + seg = seg->next; + } + fail(); + return 0; +} + +/** Get the tcplen (datalen + SYN/FIN) of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return tcplen of the segment + */ +static int +tcp_oos_seg_tcplen(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return TCP_TCPLEN(seg); + } + num++; + seg = seg->next; + } + fail(); + return -1; +} + +/** Get the tcplen (datalen + SYN/FIN) of all segments on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @return tcplen of all segment + */ +static int +tcp_oos_tcplen(struct tcp_pcb* pcb) +{ + int len = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + len += TCP_TCPLEN(seg); + seg = seg->next; + } + return len; +} + +/* Setup/teardown functions */ + +static void +tcp_oos_setup(void) +{ + tcp_remove_all(); +} + +static void +tcp_oos_teardown(void) +{ + tcp_remove_all(); + netif_list = NULL; + netif_default = NULL; +} + + + +/* Test functions */ + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received in out-of-sequence segments only */ +START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_8_9, *p_4_8, *p_4_10, *p_2_14, *p_fin, *pinseq; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p1: 8 bytes before FIN */ + /* seqno: 8..16 */ + p_8_9 = tcp_create_rx_segment(pcb, &data[8], 8, 8, 0, TCP_ACK|TCP_FIN); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer */ + /* seqno: 4..13 */ + p_4_10 = tcp_create_rx_segment(pcb, &data[4], 10, 4, 0, TCP_ACK); + /* p4: 14 bytes before FIN, includes data from p1 and p2, plus partly from pinseq */ + /* seqno: 2..15 */ + p_2_14 = tcp_create_rx_segment(pcb, &data[2], 14, 2, 0, TCP_ACK); + /* FIN, seqno 16 */ + p_fin = tcp_create_rx_segment(pcb, NULL, 0,16, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_8_9 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_4_10 != NULL); + EXPECT(p_2_14 != NULL); + EXPECT(p_fin != NULL); + if ((pinseq != NULL) && (p_8_9 != NULL) && (p_4_8 != NULL) && (p_4_10 != NULL) && (p_2_14 != NULL) && (p_fin != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_8_9, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_10, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_14, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_fin, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received IN-SEQUENCE at the end */ +START_TEST(test_tcp_recv_ooseq_FIN_INSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_1_2, *p_4_8, *p_3_11, *p_2_12, *p_15_1, *p_15_1a, *pinseq, *pinseqFIN; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* p1: 7 bytes - 2 before FIN */ + /* seqno: 1..2 */ + p_1_2 = tcp_create_rx_segment(pcb, &data[1], 2, 1, 0, TCP_ACK); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer and one byte more at the front */ + /* seqno: 3..13 */ + p_3_11 = tcp_create_rx_segment(pcb, &data[3], 11, 3, 0, TCP_ACK); + /* p4: 13 bytes - 2 before FIN - should be ignored as contained in p1 and p3 */ + /* seqno: 2..13 */ + p_2_12 = tcp_create_rx_segment(pcb, &data[2], 12, 2, 0, TCP_ACK); + /* pinseq is the first segment that is held back to create ooseq! */ + /* seqno: 0..3 */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p5: last byte before FIN */ + /* seqno: 15 */ + p_15_1 = tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* p6: same as p5, should be ignored */ + p_15_1a= tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* pinseqFIN: last 2 bytes plus FIN */ + /* only segment containing seqno 14 and FIN */ + pinseqFIN = tcp_create_rx_segment(pcb, &data[14], 2, 14, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_1_2 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_3_11 != NULL); + EXPECT(p_2_12 != NULL); + EXPECT(p_15_1 != NULL); + EXPECT(p_15_1a != NULL); + EXPECT(pinseqFIN != NULL); + if ((pinseq != NULL) && (p_1_2 != NULL) && (p_4_8 != NULL) && (p_3_11 != NULL) && (p_2_12 != NULL) + && (p_15_1 != NULL) && (p_15_1a != NULL) && (pinseqFIN != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_1_2, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 8); + + /* pass the segment to tcp_input */ + test_tcp_input(p_3_11, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + /* p_3_11 has removed p_4_8 from ooseq */ + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 3); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 11); + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_12, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 12); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1a, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseqFIN, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 2); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +static char data_full_wnd[TCP_WND]; + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +START_TEST(test_tcp_recv_ooseq_overrun_rxwin) +{ +#if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *pinseq, *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + + for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) { + int count, expected_datalen; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], + TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(p != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_count(pcb); + EXPECT_OOSEQ(count == k+1); + datalen = tcp_oos_tcplen(pcb); + if (i + TCP_MSS < TCP_WND) { + expected_datalen = (k+1)*TCP_MSS; + } else { + expected_datalen = TCP_WND - TCP_MSS; + } + if (datalen != expected_datalen) { + EXPECT_OOSEQ(datalen == expected_datalen); + } + } + + /* pass in one more segment, cleary overrunning the rxwin */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == k); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == datalen2); + + /* now pass inseq */ + test_tcp_input(pinseq, &netif); + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_bytes) +{ +#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(k = 1, i = 1; k < TCP_OOSEQ_MAX_BYTES; k += TCP_MSS, i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[k], + TCP_MSS, k, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->next == NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == (i * TCP_MSS)); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[k+1], 1, k+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == ((i-1) * TCP_MSS)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_pbufs) +{ +#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(i = 1; i <= TCP_OOSEQ_MAX_PBUFS; i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[i], + 1, i, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->next == NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == i); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[i+1], 1, i+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == (i-1)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +static void +check_rx_counters(struct tcp_pcb *pcb, struct test_tcp_counters *counters, u32_t exp_close_calls, u32_t exp_rx_calls, + u32_t exp_rx_bytes, u32_t exp_err_calls, int exp_oos_count, int exp_oos_len) +{ + int oos_len; + EXPECT(counters->close_calls == exp_close_calls); + EXPECT(counters->recv_calls == exp_rx_calls); + EXPECT(counters->recved_bytes == exp_rx_bytes); + EXPECT(counters->err_calls == exp_err_calls); + /* check that pbuf is queued in ooseq */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == exp_oos_count); + oos_len = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(exp_oos_len == oos_len); +} + +/* this test uses 4 packets: + * - data (len=TCP_MSS) + * - FIN + * - data after FIN (len=1) (invalid) + * - 2nd FIN (invalid) + * + * the parameter 'delay_packet' is a bitmask that choses which on these packets is ooseq + */ +static void test_tcp_recv_ooseq_double_FINs(int delay_packet) +{ + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0; + int first_dropped = 0xff; + int last_dropped = 0; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* create segments */ + p = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + p_normal_fin = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS, 0, TCP_ACK|TCP_FIN); + k = 1; + p_data_after_fin = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS+1], k, TCP_MSS+1, 0, TCP_ACK); + p_2nd_fin_ooseq = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS+1+k, 0, TCP_ACK|TCP_FIN); + + if(delay_packet & 1) { + /* drop normal data */ + first_dropped = 1; + last_dropped = 1; + } else { + /* send normal data */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* drop FIN */ + if(first_dropped > 2) { + first_dropped = 2; + } + last_dropped = 2; + } else { + /* send FIN */ + test_tcp_input(p_normal_fin, &netif); + if (first_dropped < 2) { + /* already dropped packets, this one is ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } else { + /* inseq */ + exp_close_calls++; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* drop data-after-FIN */ + if(first_dropped > 3) { + first_dropped = 3; + } + last_dropped = 3; + } else { + /* send data-after-FIN */ + test_tcp_input(p_data_after_fin, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen += k; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* drop 2nd-FIN */ + if(first_dropped > 4) { + first_dropped = 4; + } + last_dropped = 4; + } else { + /* send 2nd-FIN */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 1) { + /* dropped normal data before */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + if((delay_packet & 2) == 0) { + /* normal FIN was NOT delayed */ + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* dropped normal FIN before */ + test_tcp_input(p_normal_fin, &netif); + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* dropped data-after-FIN before */ + test_tcp_input(p_data_after_fin, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* dropped 2nd-FIN before */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + /* check that ooseq data has been dumped */ + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +#define FIN_TEST(name, num) \ + START_TEST(name) \ + { \ + LWIP_UNUSED_ARG(_i); \ + test_tcp_recv_ooseq_double_FINs(num); \ + } \ + END_TEST +FIN_TEST(test_tcp_recv_ooseq_double_FIN_0, 0) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_1, 1) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_2, 2) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_3, 3) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_4, 4) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_5, 5) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_6, 6) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_7, 7) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_8, 8) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_9, 9) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_10, 10) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_11, 11) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_12, 12) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_13, 13) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_14, 14) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_15, 15) + + +/** Create the suite including all tests for this module */ +Suite * +tcp_oos_suite(void) +{ + TFun tests[] = { + test_tcp_recv_ooseq_FIN_OOSEQ, + test_tcp_recv_ooseq_FIN_INSEQ, + test_tcp_recv_ooseq_overrun_rxwin, + test_tcp_recv_ooseq_max_bytes, + test_tcp_recv_ooseq_max_pbufs, + test_tcp_recv_ooseq_double_FIN_0, + test_tcp_recv_ooseq_double_FIN_1, + test_tcp_recv_ooseq_double_FIN_2, + test_tcp_recv_ooseq_double_FIN_3, + test_tcp_recv_ooseq_double_FIN_4, + test_tcp_recv_ooseq_double_FIN_5, + test_tcp_recv_ooseq_double_FIN_6, + test_tcp_recv_ooseq_double_FIN_7, + test_tcp_recv_ooseq_double_FIN_8, + test_tcp_recv_ooseq_double_FIN_9, + test_tcp_recv_ooseq_double_FIN_10, + test_tcp_recv_ooseq_double_FIN_11, + test_tcp_recv_ooseq_double_FIN_12, + test_tcp_recv_ooseq_double_FIN_13, + test_tcp_recv_ooseq_double_FIN_14, + test_tcp_recv_ooseq_double_FIN_15 + }; + return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(TFun), tcp_oos_setup, tcp_oos_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.h b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.h new file mode 100644 index 0000000..5e411f0 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/tcp/test_tcp_oos.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_OOS_H__ +#define __TEST_TCP_OOS_H__ + +#include "../lwip_check.h" + +Suite *tcp_oos_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.c b/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.c new file mode 100644 index 0000000..a2f02af --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.c @@ -0,0 +1,68 @@ +#include "test_udp.h" + +#include "lwip/udp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS +#error "This tests needs UDP- and MEMP-statistics enabled" +#endif + +/* Helper functions */ +static void +udp_remove_all(void) +{ + struct udp_pcb *pcb = udp_pcbs; + struct udp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + udp_remove(pcb2); + } + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); +} + +/* Setups/teardown functions */ + +static void +udp_setup(void) +{ + udp_remove_all(); +} + +static void +udp_teardown(void) +{ + udp_remove_all(); +} + + +/* Test functions */ + +START_TEST(test_udp_new_remove) +{ + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 1); + udp_remove(pcb); + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +udp_suite(void) +{ + TFun tests[] = { + test_udp_new_remove, + }; + return create_suite("UDP", tests, sizeof(tests)/sizeof(TFun), udp_setup, udp_teardown); +} diff --git a/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.h b/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.h new file mode 100644 index 0000000..9335368 --- /dev/null +++ b/app/src/main/jni/badvpn/lwip/test/unit/udp/test_udp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_UDP_H__ +#define __TEST_UDP_H__ + +#include "../lwip_check.h" + +Suite* udp_suite(void); + +#endif diff --git a/app/src/main/jni/badvpn/misc/BRefTarget.h b/app/src/main/jni/badvpn/misc/BRefTarget.h new file mode 100644 index 0000000..4324605 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/BRefTarget.h @@ -0,0 +1,114 @@ +/** + * @file BRefTarget.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_REF_TARGET_H +#define BADVPN_B_REF_TARGET_H + +#include + +#include +#include + +/** + * Represents a reference-counted object. + */ +typedef struct BRefTarget_s BRefTarget; + +/** + * Callback function called after the reference count of a {@link BRefTarget} + * reaches has reached zero. At this point the BRefTarget object has already + * been invalidated, i.e. {@link BRefTarget_Ref} must not be called on this + * object after this handler is called. + */ +typedef void (*BRefTarget_func_release) (BRefTarget *o); + +struct BRefTarget_s { + BRefTarget_func_release func_release; + int refcnt; + DebugObject d_obj; +}; + +/** + * Initializes a reference target object. The initial reference count of the object + * is 1. The \a func_release argument specifies the function to be called from + * {@link BRefTarget_Deref} when the reference count reaches zero. + */ +static void BRefTarget_Init (BRefTarget *o, BRefTarget_func_release func_release); + +/** + * Decrements the reference count of a reference target object. If the reference + * count has reached zero, the object's {@link BRefTarget_func_release} function + * is called, and the object is considered destroyed. + */ +static void BRefTarget_Deref (BRefTarget *o); + +/** + * Increments the reference count of a reference target object. + * Returns 1 on success and 0 on failure. + */ +static int BRefTarget_Ref (BRefTarget *o) WARN_UNUSED; + +static void BRefTarget_Init (BRefTarget *o, BRefTarget_func_release func_release) +{ + ASSERT(func_release) + + o->func_release = func_release; + o->refcnt = 1; + + DebugObject_Init(&o->d_obj); +} + +static void BRefTarget_Deref (BRefTarget *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->refcnt > 0) + + o->refcnt--; + + if (o->refcnt == 0) { + DebugObject_Free(&o->d_obj); + o->func_release(o); + } +} + +static int BRefTarget_Ref (BRefTarget *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->refcnt > 0) + + if (o->refcnt == INT_MAX) { + return 0; + } + + o->refcnt++; + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/Utf16Decoder.h b/app/src/main/jni/badvpn/misc/Utf16Decoder.h new file mode 100644 index 0000000..819ac94 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/Utf16Decoder.h @@ -0,0 +1,113 @@ +/** + * @file Utf16Decoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF16DECODER_H +#define BADVPN_UTF16DECODER_H + +#include + +#include + +/** + * Decodes UTF-16 data into Unicode characters. + */ +typedef struct { + int cont; + uint32_t ch; +} Utf16Decoder; + +/** + * Initializes the UTF-16 decoder. + * + * @param o the object + */ +static void Utf16Decoder_Init (Utf16Decoder *o); + +/** + * Inputs a 16-bit value to the decoder. + * + * @param o the object + * @param b 16-bit value to input + * @param out_ch will receive a Unicode character if this function returns 1. + * If written, the character will be in the range 0 - 0x10FFFF, + * excluding the surrogate range 0xD800 - 0xDFFF. + * @return 1 if a Unicode character has been written to *out_ch, 0 if not + */ +static int Utf16Decoder_Input (Utf16Decoder *o, uint16_t b, uint32_t *out_ch); + +void Utf16Decoder_Init (Utf16Decoder *o) +{ + o->cont = 0; +} + +int Utf16Decoder_Input (Utf16Decoder *o, uint16_t b, uint32_t *out_ch) +{ + // high surrogate + if (b >= UINT16_C(0xD800) && b <= UINT16_C(0xDBFF)) { + // set continuation state + o->cont = 1; + + // add high bits + o->ch = (uint32_t)(b - UINT16_C(0xD800)) << 10; + + return 0; + } + + // low surrogate + if (b >= UINT16_C(0xDC00) && b <= UINT16_C(0xDFFF)) { + // check continuation + if (!o->cont) { + return 0; + } + + // add low bits + o->ch |= (b - UINT16_C(0xDC00)); + + // reset state + o->cont = 0; + + // don't report surrogates + if (o->ch >= UINT32_C(0xD800) && o->ch <= UINT32_C(0xDFFF)) { + return 0; + } + + // return character + *out_ch = o->ch; + return 1; + } + + // reset state + o->cont = 0; + + // return character + *out_ch = b; + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/Utf16Encoder.h b/app/src/main/jni/badvpn/misc/Utf16Encoder.h new file mode 100644 index 0000000..4900b42 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/Utf16Encoder.h @@ -0,0 +1,67 @@ +/** + * @file Utf16Encoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF16ENCODER_H +#define BADVPN_UTF16ENCODER_H + +#include + +/** + * Encodes a Unicode character into a sequence of 16-bit values according to UTF-16. + * + * @param ch Unicode character to encode + * @param out will receive the encoded 16-bit values. Must have space for 2 values. + * @return number of 16-bit values written, 0-2, with 0 meaning the character cannot + * be encoded + */ +static int Utf16Encoder_EncodeCharacter (uint32_t ch, uint16_t *out); + +int Utf16Encoder_EncodeCharacter (uint32_t ch, uint16_t *out) +{ + if (ch <= UINT32_C(0xFFFF)) { + // surrogates + if (ch >= UINT32_C(0xD800) && ch <= UINT32_C(0xDFFF)) { + return 0; + } + + out[0] = ch; + return 1; + } + + if (ch <= UINT32_C(0x10FFFF)) { + uint32_t x = ch - UINT32_C(0x10000); + out[0] = UINT32_C(0xD800) + (x >> 10); + out[1] = UINT32_C(0xDC00) + (x & UINT32_C(0x3FF)); + return 2; + } + + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/Utf8Decoder.h b/app/src/main/jni/badvpn/misc/Utf8Decoder.h new file mode 100644 index 0000000..c6412b1 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/Utf8Decoder.h @@ -0,0 +1,143 @@ +/** + * @file Utf8Decoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF8DECODER_H +#define BADVPN_UTF8DECODER_H + +#include + +#include + +/** + * Decodes UTF-8 data into Unicode characters. + */ +typedef struct { + int bytes; + int pos; + uint32_t ch; +} Utf8Decoder; + +/** + * Initializes the UTF-8 decoder. + * + * @param o the object + */ +static void Utf8Decoder_Init (Utf8Decoder *o); + +/** + * Inputs a byte to the decoder. + * + * @param o the object + * @param b byte to input + * @param out_ch will receive a Unicode character if this function returns 1. + * If written, the character will be in the range 0 - 0x10FFFF, + * excluding the surrogate range 0xD800 - 0xDFFF. + * @return 1 if a Unicode character has been written to *out_ch, 0 if not + */ +static int Utf8Decoder_Input (Utf8Decoder *o, uint8_t b, uint32_t *out_ch); + +void Utf8Decoder_Init (Utf8Decoder *o) +{ + o->bytes = 0; +} + +int Utf8Decoder_Input (Utf8Decoder *o, uint8_t b, uint32_t *out_ch) +{ + // one-byte character + if ((b & 128) == 0) { + o->bytes = 0; + *out_ch = b; + return 1; + } + + // start of two-byte character + if ((b & 224) == 192) { + o->bytes = 2; + o->pos = 1; + o->ch = (uint32_t)(b & 31) << 6; + return 0; + } + + // start of three-byte character + if ((b & 240) == 224) { + o->bytes = 3; + o->pos = 1; + o->ch = (uint32_t)(b & 15) << 12; + return 0; + } + + // start of four-byte character + if ((b & 248) == 240) { + o->bytes = 4; + o->pos = 1; + o->ch = (uint32_t)(b & 7) << 18; + return 0; + } + + // continuation of multi-byte character + if ((b & 192) == 128 && o->bytes > 0) { + ASSERT(o->bytes <= 4) + ASSERT(o->pos > 0) + ASSERT(o->pos < o->bytes) + + // add bits from this byte + o->ch |= (uint32_t)(b & 63) << (6 * (o->bytes - o->pos - 1)); + + // end of multi-byte character? + if (o->pos == o->bytes - 1) { + // reset state + o->bytes = 0; + + // don't report out-of-range characters + if (o->ch > UINT32_C(0x10FFFF)) { + return 0; + } + + // don't report surrogates + if (o->ch >= UINT32_C(0xD800) && o->ch <= UINT32_C(0xDFFF)) { + return 0; + } + + *out_ch = o->ch; + return 1; + } + + // increment byte index + o->pos++; + + return 0; + } + + // error, reset state + o->bytes = 0; + + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/Utf8Encoder.h b/app/src/main/jni/badvpn/misc/Utf8Encoder.h new file mode 100644 index 0000000..00eb5e6 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/Utf8Encoder.h @@ -0,0 +1,81 @@ +/** + * @file Utf8Encoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF8ENCODER_H +#define BADVPN_UTF8ENCODER_H + +#include + +/** + * Encodes a Unicode character into a sequence of bytes according to UTF-8. + * + * @param ch Unicode character to encode + * @param out will receive the encoded bytes. Must have space for 4 bytes. + * @return number of bytes written, 0-4, with 0 meaning the character cannot + * be encoded + */ +static int Utf8Encoder_EncodeCharacter (uint32_t ch, uint8_t *out); + +int Utf8Encoder_EncodeCharacter (uint32_t ch, uint8_t *out) +{ + if (ch <= UINT32_C(0x007F)) { + out[0] = ch; + return 1; + } + + if (ch <= UINT32_C(0x07FF)) { + out[0] = (0xC0 | (ch >> 6)); + out[1] = (0x80 | ((ch >> 0) & 0x3F)); + return 2; + } + + if (ch <= UINT32_C(0xFFFF)) { + // surrogates + if (ch >= UINT32_C(0xD800) && ch <= UINT32_C(0xDFFF)) { + return 0; + } + + out[0] = (0xE0 | (ch >> 12)); + out[1] = (0x80 | ((ch >> 6) & 0x3F)); + out[2] = (0x80 | ((ch >> 0) & 0x3F)); + return 3; + } + + if (ch < UINT32_C(0x10FFFF)) { + out[0] = (0xF0 | (ch >> 18)); + out[1] = (0x80 | ((ch >> 12) & 0x3F)); + out[2] = (0x80 | ((ch >> 6) & 0x3F)); + out[3] = (0x80 | ((ch >> 0) & 0x3F)); + return 4; + } + + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/arp_proto.h b/app/src/main/jni/badvpn/misc/arp_proto.h new file mode 100644 index 0000000..71a6c98 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/arp_proto.h @@ -0,0 +1,60 @@ +/** + * @file arp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the ARP protocol. + */ + +#ifndef BADVPN_ARP_PROTO_H +#define BADVPN_ARP_PROTO_H + +#include + +#include + +#define ARP_HARDWARE_TYPE_ETHERNET 1 + +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 + +B_START_PACKED +struct arp_packet { + uint16_t hardware_type; + uint16_t protocol_type; + uint8_t hardware_size; + uint8_t protocol_size; + uint16_t opcode; + uint8_t sender_mac[6]; + uint32_t sender_ip; + uint8_t target_mac[6]; + uint32_t target_ip; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/misc/array_length.h b/app/src/main/jni/badvpn/misc/array_length.h new file mode 100644 index 0000000..15de964 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/array_length.h @@ -0,0 +1,35 @@ +/** + * @file array_length.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_ARRAY_LENGTH +#define BADVPN_ARRAY_LENGTH + +#define B_ARRAY_LENGTH(arr) (sizeof((arr)) / sizeof((arr)[0])) + +#endif diff --git a/app/src/main/jni/badvpn/misc/balign.h b/app/src/main/jni/badvpn/misc/balign.h new file mode 100644 index 0000000..57152af --- /dev/null +++ b/app/src/main/jni/badvpn/misc/balign.h @@ -0,0 +1,76 @@ +/** + * @file balign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Integer alignment macros. + */ + +#ifndef BADVPN_MISC_BALIGN_H +#define BADVPN_MISC_BALIGN_H + +#include +#include + +/** + * Checks if aligning x up to n would overflow. + */ +static int balign_up_overflows (size_t x, size_t n) +{ + size_t r = x % n; + + return (r && x > SIZE_MAX - (n - r)); +} + +/** + * Aligns x up to n. + */ +static size_t balign_up (size_t x, size_t n) +{ + size_t r = x % n; + return (r ? x + (n - r) : x); +} + +/** + * Aligns x down to n. + */ +static size_t balign_down (size_t x, size_t n) +{ + return (x - (x % n)); +} + +/** + * Calculates the quotient of a and b, rounded up. + */ +static size_t bdivide_up (size_t a, size_t b) +{ + size_t r = a % b; + return (r > 0 ? a / b + 1 : a / b); +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/balloc.h b/app/src/main/jni/badvpn/misc/balloc.h new file mode 100644 index 0000000..7d2d54f --- /dev/null +++ b/app/src/main/jni/badvpn/misc/balloc.h @@ -0,0 +1,248 @@ +/** + * @file balloc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Memory allocation functions. + */ + +#ifndef BADVPN_MISC_BALLOC_H +#define BADVPN_MISC_BALLOC_H + +#include +#include +#include +#include + +#include +#include +#include + +/** + * Allocates memory. + * + * @param bytes number of bytes to allocate. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAlloc (size_t bytes); + +/** + * Frees memory. + * + * @param m memory to free. Must have been obtained with {@link BAlloc}, + * {@link BAllocArray}, or {@link BAllocArray2}. May be NULL; + * in this case, this function does nothing. + */ +static void BFree (void *m); + +/** + * Changes the size of a memory block. On success, the memory block + * may be moved to a different address. + * + * @param m pointer to a memory block obtained from {@link BAlloc} + * or other functions in this group. If this is NULL, the + * call is equivalent to {@link BAlloc}(bytes). + * @param bytes new size of the memory block + * @return new pointer to the memory block, or NULL on failure + */ +static void * BRealloc (void *m, size_t bytes); + +/** + * Allocates memory, with size given as a {@link bsize_t}. + * + * @param bytes number of bytes to allocate. If the size is overflow, + * this function will return NULL. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocSize (bsize_t bytes); + +/** + * Allocates memory for an array. + * A check is first done to make sure the multiplication doesn't overflow; + * otherwise, this is equivalent to {@link BAlloc}(count * bytes). + * This may be slightly faster if 'bytes' is constant, because a division + * with 'bytes' is performed. + * + * @param count number of elements. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray (size_t count, size_t bytes); + +/** + * Reallocates memory that was allocated using one of the allocation + * functions in this file. On success, the memory may be moved to a + * different address, leaving the old address invalid. + * + * @param mem pointer to an existing memory block. May be NULL, in which + * case this is equivalent to {@link BAllocArray}. + * @param count number of elements for reallocation + * @param bytes size of one array element for reallocation + * @return a non-NULL pointer to the address of the reallocated memory + * block, or NULL on failure. On failure, the original memory + * block is left intact. + */ +static void * BReallocArray (void *mem, size_t count, size_t bytes); + +/** + * Allocates memory for a two-dimensional array. + * + * Checks are first done to make sure the multiplications don't overflow; + * otherwise, this is equivalent to {@link BAlloc}((count2 * (count1 * bytes)). + * + * @param count2 number of elements in one dimension. + * @param count1 number of elements in the other dimension. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray2 (size_t count2, size_t count1, size_t bytes); + +/** + * Adds to a size_t with overflow detection. + * + * @param s pointer to a size_t to add to + * @param add number to add + * @return 1 on success, 0 on failure + */ +static int BSizeAdd (size_t *s, size_t add); + +/** + * Aligns a size_t upwards with overflow detection. + * + * @param s pointer to a size_t to align + * @param align alignment value. Must be >0. + * @return 1 on success, 0 on failure + */ +static int BSizeAlign (size_t *s, size_t align); + +void * BAlloc (size_t bytes) +{ + if (bytes == 0) { + return malloc(1); + } + + return malloc(bytes); +} + +void BFree (void *m) +{ + free(m); +} + +void * BRealloc (void *m, size_t bytes) +{ + if (bytes == 0) { + return realloc(m, 1); + } + + return realloc(m, bytes); +} + +void * BAllocSize (bsize_t bytes) +{ + if (bytes.is_overflow) { + return NULL; + } + + return BAlloc(bytes.value); +} + +void * BAllocArray (size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return malloc(1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return BAlloc(count * bytes); +} + +void * BReallocArray (void *mem, size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return realloc(mem, 1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return realloc(mem, count * bytes); +} + +void * BAllocArray2 (size_t count2, size_t count1, size_t bytes) +{ + if (count2 == 0 || count1 == 0 || bytes == 0) { + return malloc(1); + } + + if (count1 > SIZE_MAX / bytes) { + return NULL; + } + + if (count2 > SIZE_MAX / (count1 * bytes)) { + return NULL; + } + + return BAlloc(count2 * (count1 * bytes)); +} + +int BSizeAdd (size_t *s, size_t add) +{ + ASSERT(s) + + if (add > SIZE_MAX - *s) { + return 0; + } + *s += add; + return 1; +} + +int BSizeAlign (size_t *s, size_t align) +{ + ASSERT(s) + ASSERT(align > 0) + + size_t mod = *s % align; + if (mod > 0) { + if (align - mod > SIZE_MAX - *s) { + return 0; + } + *s += align - mod; + } + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/blimits.h b/app/src/main/jni/badvpn/misc/blimits.h new file mode 100644 index 0000000..8bcdc2a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/blimits.h @@ -0,0 +1,60 @@ +/** + * @file blimits.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BLIMITS_H +#define BADVPN_BLIMITS_H + +#include + +#define BTYPE_IS_SIGNED(type) ((type)-1 < 0) + +#define BSIGNED_TYPE_MIN(type) ( \ + sizeof(type) == 1 ? INT8_MIN : ( \ + sizeof(type) == 2 ? INT16_MIN : ( \ + sizeof(type) == 4 ? INT32_MIN : ( \ + sizeof(type) == 8 ? INT64_MIN : 0)))) + +#define BSIGNED_TYPE_MAX(type) ( \ + sizeof(type) == 1 ? INT8_MAX : ( \ + sizeof(type) == 2 ? INT16_MAX : ( \ + sizeof(type) == 4 ? INT32_MAX : ( \ + sizeof(type) == 8 ? INT64_MAX : 0)))) + +#define BUNSIGNED_TYPE_MIN(type) ((type)0) + +#define BUNSIGNED_TYPE_MAX(type) ( \ + sizeof(type) == 1 ? UINT8_MAX : ( \ + sizeof(type) == 2 ? UINT16_MAX : ( \ + sizeof(type) == 4 ? UINT32_MAX : ( \ + sizeof(type) == 8 ? UINT64_MAX : 0)))) + +#define BTYPE_MIN(type) (BTYPE_IS_SIGNED(type) ? BSIGNED_TYPE_MIN(type) : BUNSIGNED_TYPE_MIN(type)) +#define BTYPE_MAX(type) (BTYPE_IS_SIGNED(type) ? BSIGNED_TYPE_MAX(type) : BUNSIGNED_TYPE_MAX(type)) + +#endif diff --git a/app/src/main/jni/badvpn/misc/bsize.h b/app/src/main/jni/badvpn/misc/bsize.h new file mode 100644 index 0000000..2d724df --- /dev/null +++ b/app/src/main/jni/badvpn/misc/bsize.h @@ -0,0 +1,117 @@ +/** + * @file bsize.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Arithmetic with overflow detection. + */ + +#ifndef BADVPN_MISC_BSIZE_H +#define BADVPN_MISC_BSIZE_H + +#include +#include +#include + +typedef struct { + int is_overflow; + size_t value; +} bsize_t; + +static bsize_t bsize_fromsize (size_t v); +static bsize_t bsize_fromint (int v); +static bsize_t bsize_overflow (void); +static int bsize_tosize (bsize_t s, size_t *out); +static int bsize_toint (bsize_t s, int *out); +static bsize_t bsize_add (bsize_t s1, bsize_t s2); +static bsize_t bsize_max (bsize_t s1, bsize_t s2); +static bsize_t bsize_mul (bsize_t s1, bsize_t s2); + +bsize_t bsize_fromsize (size_t v) +{ + bsize_t s = {0, v}; + return s; +} + +bsize_t bsize_fromint (int v) +{ + bsize_t s = {(v < 0 || v > SIZE_MAX), v}; + return s; +} + +static bsize_t bsize_overflow (void) +{ + bsize_t s = {1, 0}; + return s; +} + +int bsize_tosize (bsize_t s, size_t *out) +{ + if (s.is_overflow) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +int bsize_toint (bsize_t s, int *out) +{ + if (s.is_overflow || s.value > INT_MAX) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +bsize_t bsize_add (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || s2.value > SIZE_MAX - s1.value), (s1.value + s2.value)}; + return s; +} + +bsize_t bsize_max (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow), ((s1.value > s2.value) * s1.value + (s1.value <= s2.value) * s2.value)}; + return s; +} + +bsize_t bsize_mul (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || (s1.value != 0 && s2.value > SIZE_MAX / s1.value)), (s1.value * s2.value)}; + return s; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/bsort.h b/app/src/main/jni/badvpn/misc/bsort.h new file mode 100644 index 0000000..24d7a66 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/bsort.h @@ -0,0 +1,69 @@ +/** + * @file bsort.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Sorting functions. + */ + +#ifndef BADVPN_MISC_BSORT_H +#define BADVPN_MISC_BSORT_H + +#include +#include +#include + +#include +#include + +typedef int (*BSort_comparator) (const void *e1, const void *e2); + +static void BInsertionSort (void *arr, size_t count, size_t esize, BSort_comparator compatator, void *temp); + +void BInsertionSort (void *arr, size_t count, size_t esize, BSort_comparator compatator, void *temp) +{ + ASSERT(esize > 0) + + for (size_t i = 0; i < count; i++) { + size_t j = i; + while (j > 0) { + uint8_t *x = (uint8_t *)arr + (j - 1) * esize; + uint8_t *y = (uint8_t *)arr + j * esize; + int c = compatator(x, y); + if (c <= 0) { + break; + } + memcpy(temp, x, esize); + memcpy(x, y, esize); + memcpy(y, temp, esize); + j--; + } + } +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/bstring.h b/app/src/main/jni/badvpn/misc/bstring.h new file mode 100644 index 0000000..caef106 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/bstring.h @@ -0,0 +1,140 @@ +/** + * @file bstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BSTRING_H +#define BADVPN_BSTRING_H + +#include + +#include +#include + +#define BSTRING_TYPE_STATIC 5 +#define BSTRING_TYPE_DYNAMIC 7 +#define BSTRING_TYPE_EXTERNAL 11 + +#define BSTRING_STATIC_SIZE 23 +#define BSTRING_STATIC_MAX (BSTRING_STATIC_SIZE - 1) + +typedef struct { + union { + struct { + char type; + char static_string[BSTRING_STATIC_SIZE]; + } us; + struct { + char type; + char *dynamic_string; + } ud; + struct { + char type; + const char *external_string; + } ue; + } u; +} BString; + +static void BString__assert (BString *o) +{ + switch (o->u.us.type) { + case BSTRING_TYPE_STATIC: + case BSTRING_TYPE_DYNAMIC: + case BSTRING_TYPE_EXTERNAL: + return; + } + + ASSERT(0); +} + +static int BString_Init (BString *o, const char *str) +{ + if (strlen(str) <= BSTRING_STATIC_MAX) { + strcpy(o->u.us.static_string, str); + o->u.us.type = BSTRING_TYPE_STATIC; + } else { + if (!(o->u.ud.dynamic_string = malloc(strlen(str) + 1))) { + return 0; + } + strcpy(o->u.ud.dynamic_string, str); + o->u.ud.type = BSTRING_TYPE_DYNAMIC; + } + + BString__assert(o); + return 1; +} + +static void BString_InitStatic (BString *o, const char *str) +{ + ASSERT(strlen(str) <= BSTRING_STATIC_MAX) + + strcpy(o->u.us.static_string, str); + o->u.us.type = BSTRING_TYPE_STATIC; + + BString__assert(o); +} + +static void BString_InitExternal (BString *o, const char *str) +{ + o->u.ue.external_string = str; + o->u.ue.type = BSTRING_TYPE_EXTERNAL; + + BString__assert(o); +} + +static void BString_InitAllocated (BString *o, char *str) +{ + o->u.ud.dynamic_string = str; + o->u.ud.type = BSTRING_TYPE_DYNAMIC; + + BString__assert(o); +} + +static void BString_Free (BString *o) +{ + BString__assert(o); + + if (o->u.ud.type == BSTRING_TYPE_DYNAMIC) { + free(o->u.ud.dynamic_string); + } +} + +static const char * BString_Get (BString *o) +{ + BString__assert(o); + + switch (o->u.us.type) { + case BSTRING_TYPE_STATIC: return o->u.us.static_string; + case BSTRING_TYPE_DYNAMIC: return o->u.ud.dynamic_string; + case BSTRING_TYPE_EXTERNAL: return o->u.ue.external_string; + } + + ASSERT(0); + return NULL; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/byteorder.h b/app/src/main/jni/badvpn/misc/byteorder.h new file mode 100644 index 0000000..055b0a5 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/byteorder.h @@ -0,0 +1,196 @@ +/** + * @file byteorder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Byte order conversion functions. + * + * hton* functions convert from host to big-endian (network) byte order. + * htol* functions convert from host to little-endian byte order. + * ntoh* functions convert from big-endian (network) to host byte order. + * ltoh* functions convert from little-endian to host byte order. + */ + +#ifndef BADVPN_MISC_BYTEORDER_H +#define BADVPN_MISC_BYTEORDER_H + +#if (defined(BADVPN_LITTLE_ENDIAN) + defined(BADVPN_BIG_ENDIAN)) != 1 +#error Unknown byte order or too many byte orders +#endif + +#include + +static uint16_t badvpn_reverse16 (uint16_t x) +{ + uint16_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+1); + *((uint8_t *)&y+1) = *((uint8_t *)&x+0); + return y; +} + +static uint32_t badvpn_reverse32 (uint32_t x) +{ + uint32_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+3); + *((uint8_t *)&y+1) = *((uint8_t *)&x+2); + *((uint8_t *)&y+2) = *((uint8_t *)&x+1); + *((uint8_t *)&y+3) = *((uint8_t *)&x+0); + return y; +} + +static uint64_t badvpn_reverse64 (uint64_t x) +{ + uint64_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+7); + *((uint8_t *)&y+1) = *((uint8_t *)&x+6); + *((uint8_t *)&y+2) = *((uint8_t *)&x+5); + *((uint8_t *)&y+3) = *((uint8_t *)&x+4); + *((uint8_t *)&y+4) = *((uint8_t *)&x+3); + *((uint8_t *)&y+5) = *((uint8_t *)&x+2); + *((uint8_t *)&y+6) = *((uint8_t *)&x+1); + *((uint8_t *)&y+7) = *((uint8_t *)&x+0); + return y; +} + +static uint8_t hton8 (uint8_t x) +{ + return x; +} + +static uint8_t htol8 (uint8_t x) +{ + return x; +} + +#if defined(BADVPN_LITTLE_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t hton32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t hton64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +static uint16_t htol16 (uint16_t x) +{ + return x; +} + +static uint32_t htol32 (uint32_t x) +{ + return x; +} + +static uint64_t htol64 (uint64_t x) +{ + return x; +} + +#elif defined(BADVPN_BIG_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return x; +} + +static uint32_t hton32 (uint32_t x) +{ + return x; +} + +static uint64_t hton64 (uint64_t x) +{ + return x; +} + +static uint16_t htol16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t htol32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t htol64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +#endif + +static uint8_t ntoh8 (uint8_t x) +{ + return hton8(x); +} + +static uint16_t ntoh16 (uint16_t x) +{ + return hton16(x); +} + +static uint32_t ntoh32 (uint32_t x) +{ + return hton32(x); +} + +static uint64_t ntoh64 (uint64_t x) +{ + return hton64(x); +} + +static uint8_t ltoh8 (uint8_t x) +{ + return htol8(x); +} + +static uint16_t ltoh16 (uint16_t x) +{ + return htol16(x); +} + +static uint32_t ltoh32 (uint32_t x) +{ + return htol32(x); +} + +static uint64_t ltoh64 (uint64_t x) +{ + return htol64(x); +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/cmdline.h b/app/src/main/jni/badvpn/misc/cmdline.h new file mode 100644 index 0000000..fba899a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/cmdline.h @@ -0,0 +1,181 @@ +/** + * @file cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Command line construction functions. + */ + +#ifndef BADVPN_MISC_CMDLINE_H +#define BADVPN_MISC_CMDLINE_H + +#include +#include + +#include +#include +#include +#include + +typedef struct { + struct ExpArray arr; + size_t n; +} CmdLine; + +static int CmdLine_Init (CmdLine *c); +static void CmdLine_Free (CmdLine *c); +static int CmdLine_Append (CmdLine *c, const char *str); +static int CmdLine_AppendNoNull (CmdLine *c, const char *str, size_t str_len); +static int CmdLine_AppendCstring (CmdLine *c, b_cstring cstr, size_t offset, size_t length); +static int CmdLine_AppendMulti (CmdLine *c, int num, ...); +static int CmdLine_Finish (CmdLine *c); +static char ** CmdLine_Get (CmdLine *c); + +static int _CmdLine_finished (CmdLine *c) +{ + return (c->n > 0 && ((char **)c->arr.v)[c->n - 1] == NULL); +} + +int CmdLine_Init (CmdLine *c) +{ + if (!ExpArray_init(&c->arr, sizeof(char *), 16)) { + return 0; + } + + c->n = 0; + + return 1; +} + +void CmdLine_Free (CmdLine *c) +{ + for (size_t i = 0; i < c->n; i++) { + free(((char **)c->arr.v)[i]); + } + + free(c->arr.v); +} + +int CmdLine_Append (CmdLine *c, const char *str) +{ + ASSERT(str) + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = strdup(str))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendNoNull (CmdLine *c, const char *str, size_t str_len) +{ + ASSERT(str) + ASSERT(!memchr(str, '\0', str_len)) + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = b_strdup_bin(str, str_len))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendCstring (CmdLine *c, b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + ASSERT(!b_cstring_memchr(cstr, offset, length, '\0', NULL)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = b_cstring_strdup(cstr, offset, length))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendMulti (CmdLine *c, int num, ...) +{ + int res = 1; + + va_list vl; + va_start(vl, num); + + for (int i = 0; i < num; i++) { + const char *str = va_arg(vl, const char *); + if (!CmdLine_Append(c, str)) { + res = 0; + break; + } + } + + va_end(vl); + + return res; +} + +int CmdLine_Finish (CmdLine *c) +{ + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + ((char **)c->arr.v)[c->n] = NULL; + + c->n++; + + return 1; +} + +char ** CmdLine_Get (CmdLine *c) +{ + ASSERT(_CmdLine_finished(c)) + + return (char **)c->arr.v; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/compare.h b/app/src/main/jni/badvpn/misc/compare.h new file mode 100644 index 0000000..8d1a1b9 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/compare.h @@ -0,0 +1,37 @@ +/** + * @file compare.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPARE_H +#define BADVPN_COMPARE_H + +#define B_COMPARE(a, b) (((a) > (b)) - ((a) < (b))) +#define B_COMPARE_COMBINE(cmp1, cmp2) ((cmp1) ? (cmp1) : (cmp2)) +#define B_COMPARE2(a, b, c, d) B_COMPARE_COMBINE(B_COMPARE((a), (b)), B_COMPARE((c), (d))) + +#endif diff --git a/app/src/main/jni/badvpn/misc/concat_strings.h b/app/src/main/jni/badvpn/misc/concat_strings.h new file mode 100644 index 0000000..83e0977 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/concat_strings.h @@ -0,0 +1,85 @@ +/** + * @file concat_strings.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for concatenating strings. + */ + +#ifndef BADVPN_MISC_CONCAT_STRINGS_H +#define BADVPN_MISC_CONCAT_STRINGS_H + +#include +#include +#include + +#include + +static char * concat_strings (int num, ...) +{ + ASSERT(num >= 0) + + // calculate sum of lengths + size_t sum = 0; + va_list ap; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + if (str_len > SIZE_MAX - 1 - sum) { + va_end(ap); + return NULL; + } + sum += str_len; + } + va_end(ap); + + // allocate memory + char *res_str = malloc(sum + 1); + if (!res_str) { + return NULL; + } + + // copy strings + sum = 0; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + memcpy(res_str + sum, str, str_len); + sum += str_len; + } + va_end(ap); + + // terminate + res_str[sum] = '\0'; + + return res_str; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/cstring.h b/app/src/main/jni/badvpn/misc/cstring.h new file mode 100644 index 0000000..bd8de75 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/cstring.h @@ -0,0 +1,347 @@ +/** + * @file cstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPOSED_STRING_H +#define BADVPN_COMPOSED_STRING_H + +#include +#include +#include + +#include +#include + +struct b_cstring_s; + +/** + * Callback function which is called by {@link b_cstring_get} to access the underlying resource. + * \a cstr points to the cstring being accessed, and the callback can use the userN members to + * retrieve any state information. + * \a offset is the offset from the beginning of the string; offset < cstr->length. + * This callback must set *\a out_length and return a pointer, representing a continuous + * region of the string that starts at the byte at index \a offset. Returning a region that + * spans past the end of the string is allowed. + */ +typedef const char * (*b_cstring_func) (const struct b_cstring_s *cstr, size_t offset, size_t *out_length); + +/** + * An abstract string which is not necessarily continuous. Given a cstring, its length + * can be determined by reading the 'length' member, and its data can be read using + * {@link b_cstring_get} (which internally invokes the {@link b_cstring_func} callback). + */ +typedef struct b_cstring_s { + size_t length; + b_cstring_func func; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user1; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user2; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user3; +} b_cstring; + +/** + * Makes a cstring pointing to a buffer. + * \a data may be NULL if \a length is 0. + */ +static b_cstring b_cstring_make_buf (const char *data, size_t length); + +/** + * Makes a cstring which represents an empty string. + */ +static b_cstring b_cstring_make_empty (void); + +/** + * Retrieves a pointer to a continuous region of the string. + * \a offset specifies the starting offset of the region to retrieve, and must be < cstr.length. + * \a maxlen specifies the maximum length of the returned region, and must be > 0. + * The length of the region will be stored in *\a out_chunk_len, and it will always be > 0. + * It is possible that the returned region spans past the end of the string, unless limited + * by \a maxlen. The pointer to the region will be returned; it will point to the byte + * at offset exactly \a offset into the string. + */ +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len); + +/** + * Retrieves the byte in the string at position \a pos. + */ +static char b_cstring_at (b_cstring cstr, size_t pos); + +/** + * Asserts that the range given by \a offset and \a length is valid for the string. + */ +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length); + +/** + * Copies a range to an external buffer. + */ +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest); + +/** + * Performs a memcmp-like operation on the given ranges of two cstrings. + */ +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length); + +/** + * Determines if a range within a string is equal to the bytes in an external buffer. + */ +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data); + +/** + * Determines if a range within a string contains the byte \a ch. + * Returns 1 if it does, and 0 if it does not. If it does contain it, and \a out_pos is not + * NULL, *\a out_pos is set to the index of the first matching byte in the range, relative + * to the beginning of the range \a offset. + */ +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos); + +/** + * Allocates a buffer for a range and copies it. The buffer is allocated using {@link BAlloc}. + * An extra null byte will be appended. On failure, returns NULL. + */ +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length); + +/** + * Macro which iterates the continuous regions of a range within a cstring. + * For reach region, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a rel_pos_var, \a chunk_data_var and \a chunk_length_var specify names of variables + * which will be available in \a body. + * \a rel_pos_var will hold the offset (size_t) of the current continuous region, relative + * to \a offset. + * \a chunk_data_var will hold a pointer (const char *) to the beginning of the region, and + * \a chunk_length_var will hold its length (size_t). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + * + * See the implementation of {@link b_cstring_copy_to_buf} for a usage example. + */ +#define B_CSTRING_LOOP_RANGE(cstr, offset, length, rel_pos_var, chunk_data_var, chunk_length_var, body) \ +{ \ + size_t rel_pos_var = 0; \ + while (rel_pos_var < (length)) { \ + size_t chunk_length_var; \ + const char *chunk_data_var = b_cstring_get((cstr), (offset) + rel_pos_var, (length) - rel_pos_var, &chunk_length_var); \ + { body } \ + rel_pos_var += chunk_length_var; \ + } \ +} + +/** + * Like {@link B_CSTRING_LOOP_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP(cstr, rel_pos_var, chunk_data_var, chunk_length_var, body) B_CSTRING_LOOP_RANGE(cstr, 0, (cstr).length, rel_pos_var, chunk_data_var, chunk_length_var, body) + +/** + * Macro which iterates the characters of a range within a cstring. + * For each character, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a char_rel_pos_var and \a char_var specify names of variables which will be + * available in \a body. + * \a char_rel_pos_var will hold the position (size_t) of the current character + * relative to \a offset. + * \a char_var will hold the current character (char). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + */ +#define B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, char_rel_pos_var, char_var, body) \ +B_CSTRING_LOOP_RANGE(cstr, offset, length, b_cstring_loop_chars_pos, b_cstring_loop_chars_chunk_data, b_cstring_loop_chars_chunk_length, { \ + for (size_t b_cstring_loop_chars_chunk_pos = 0; b_cstring_loop_chars_chunk_pos < b_cstring_loop_chars_chunk_length; b_cstring_loop_chars_chunk_pos++) { \ + char char_rel_pos_var = b_cstring_loop_chars_pos + b_cstring_loop_chars_chunk_pos; \ + B_USE(char_rel_pos_var) \ + char char_var = b_cstring_loop_chars_chunk_data[b_cstring_loop_chars_chunk_pos]; \ + { body } \ + } \ +}) + +/** + * Like {@link B_CSTRING_LOOP_CHARS_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP_CHARS(cstr, char_rel_pos_var, char_var, body) B_CSTRING_LOOP_CHARS_RANGE(cstr, 0, (cstr).length, char_rel_pos_var, char_var, body) + +static const char * b_cstring__buf_func (const b_cstring *cstr, size_t offset, size_t *out_length) +{ + ASSERT(offset < cstr->length) + ASSERT(out_length) + ASSERT(cstr->func == b_cstring__buf_func) + ASSERT(cstr->user1.ptr) + + *out_length = cstr->length - offset; + return (const char *)cstr->user1.ptr + offset; +} + +static b_cstring b_cstring_make_buf (const char *data, size_t length) +{ + ASSERT(length == 0 || data) + + b_cstring cstr; + cstr.length = length; + cstr.func = b_cstring__buf_func; + cstr.user1.ptr = (void *)data; + return cstr; +} + +static b_cstring b_cstring_make_empty (void) +{ + b_cstring cstr; + cstr.length = 0; + cstr.func = NULL; + return cstr; +} + +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len) +{ + ASSERT(offset < cstr.length) + ASSERT(maxlen > 0) + ASSERT(out_chunk_len) + ASSERT(cstr.func) + + const char *data = cstr.func(&cstr, offset, out_chunk_len); + ASSERT(data) + ASSERT(*out_chunk_len > 0) + + if (*out_chunk_len > maxlen) { + *out_chunk_len = maxlen; + } + + return data; +} + +static char b_cstring_at (b_cstring cstr, size_t pos) +{ + ASSERT(pos < cstr.length) + ASSERT(cstr.func) + + size_t chunk_len; + const char *data = cstr.func(&cstr, pos, &chunk_len); + ASSERT(data) + ASSERT(chunk_len > 0) + + return *data; +} + +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length) +{ + ASSERT(offset <= cstr.length) + ASSERT(length <= cstr.length - offset) +} + +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest) +{ + b_cstring_assert_range(cstr, offset, length); + ASSERT(length == 0 || dest) + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + memcpy(dest + pos, chunk_data, chunk_length); + }) +} + +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length) +{ + b_cstring_assert_range(cstr1, offset1, length); + b_cstring_assert_range(cstr2, offset2, length); + + B_CSTRING_LOOP_RANGE(cstr1, offset1, length, pos1, chunk_data1, chunk_len1, { + B_CSTRING_LOOP_RANGE(cstr2, offset2 + pos1, chunk_len1, pos2, chunk_data2, chunk_len2, { + int cmp = memcmp(chunk_data1 + pos2, chunk_data2, chunk_len2); + if (cmp) { + return cmp; + } + }) + }) + + return 0; +} + +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_len, { + if (memcmp(chunk_data, data + pos, chunk_len)) { + return 0; + } + }) + + return 1; +} + +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, cur_ch_pos, cur_ch, { + if (cur_ch == ch) { + if (out_pos) { + *out_pos = cur_ch_pos; + } + return 1; + } + }) + + return 0; +} + +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == SIZE_MAX) { + return NULL; + } + + char *buf = (char *)BAlloc(length + 1); + if (buf) { + b_cstring_copy_to_buf(cstr, offset, length, buf); + buf[length] = '\0'; + } + + return buf; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/dead.h b/app/src/main/jni/badvpn/misc/dead.h new file mode 100644 index 0000000..7f57421 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/dead.h @@ -0,0 +1,134 @@ +/** + * @file dead.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dead mechanism definitions. + * + * The dead mechanism is a way for a piece of code to detect whether some + * specific event has occured during some operation (usually during calling + * a user-provided handler function), without requiring access to memory + * that might no longer be available because of the event. + * + * It works somehow like that: + * + * First a dead variable ({@link dead_t}) is allocated somewhere, and + * initialized with {@link DEAD_INIT}, e.g.: + * DEAD_INIT(dead); + * + * When the event that needs to be caught occurs, {@link DEAD_KILL} is + * called, e.g.: + * DEAD_KILL(dead); + * The memory used by the dead variable is no longer needed after + * that. + * + * If a piece of code needs to know whether the event occured during some + * operation (but it must not have occured before!), it puts {@link DEAD_ENTER}} + * in front of the operation, and does {@link DEAD_LEAVE} at the end. If + * {@link DEAD_LEAVE} returned nonzero, the event occured, otherwise it did + * not. Example: + * DEAD_ENTER(dead) + * HandlerFunction(); + * if (DEAD_LEAVE(dead)) { + * (event occured) + * } + * + * If is is needed to check for the event more than once in a single block, + * {@link DEAD_DECLARE} should be put somewhere before, and {@link DEAD_ENTER2} + * should be used instead of {@link DEAD_ENTER}. + * + * If it is needed to check for multiple events (dead variables) at the same + * time, DEAD_*_N macros should be used instead, specifying different + * identiers as the first argument for different dead variables. + */ + +#ifndef BADVPN_MISC_DEAD_H +#define BADVPN_MISC_DEAD_H + +#include + +/** + * Dead variable. + */ +typedef int *dead_t; + +/** + * Initializes a dead variable. + */ +#define DEAD_INIT(ptr) { ptr = NULL; } + +/** + * Kills the dead variable, + */ +#define DEAD_KILL(ptr) { if (ptr) *(ptr) = 1; } + +/** + * Kills the dead variable with the given value, or does nothing + * if the value is 0. The value will seen by {@link DEAD_KILLED}. + */ +#define DEAD_KILL_WITH(ptr, val) { if (ptr) *(ptr) = (val); } + +/** + * Declares dead catching variables. + */ +#define DEAD_DECLARE int badvpn__dead; dead_t badvpn__prev_ptr; + +/** + * Enters a dead catching using already declared dead catching variables. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER2(ptr) { badvpn__dead = 0; badvpn__prev_ptr = ptr; ptr = &badvpn__dead; } + +/** + * Declares dead catching variables and enters a dead catching. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER(ptr) DEAD_DECLARE DEAD_ENTER2(ptr) + +/** + * Leaves a dead catching. + */ +#define DEAD_LEAVE2(ptr) { if (!badvpn__dead) ptr = badvpn__prev_ptr; if (badvpn__prev_ptr) *badvpn__prev_ptr = badvpn__dead; } + +/** + * Returns 1 if {@link DEAD_KILL} was called for the dead variable, 0 otherwise. + * Must be called after entering a dead catching. + */ +#define DEAD_KILLED (badvpn__dead) + +#define DEAD_DECLARE_N(n) int badvpn__dead##n; dead_t badvpn__prev_ptr##n; +#define DEAD_ENTER2_N(n, ptr) { badvpn__dead##n = 0; badvpn__prev_ptr##n = ptr; ptr = &badvpn__dead##n;} +#define DEAD_ENTER_N(n, ptr) DEAD_DECLARE_N(n) DEAD_ENTER2_N(n, ptr) +#define DEAD_LEAVE2_N(n, ptr) { if (!badvpn__dead##n) ptr = badvpn__prev_ptr##n; if (badvpn__prev_ptr##n) *badvpn__prev_ptr##n = badvpn__dead##n; } +#define DEAD_KILLED_N(n) (badvpn__dead##n) + +#endif diff --git a/app/src/main/jni/badvpn/misc/debug.h b/app/src/main/jni/badvpn/misc/debug.h new file mode 100644 index 0000000..13bc866 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/debug.h @@ -0,0 +1,142 @@ +/** + * @file debug.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Debugging macros. + */ + +/** + * @def DEBUG + * + * Macro for printing debugging text. Use the same way as printf, + * but without a newline. + * Prepends "function_name: " and appends a newline. + */ + +/** + * @def ASSERT_FORCE + * + * Macro for forced assertions. + * Evaluates the argument and terminates the program abnormally + * if the result is false. + */ + +/** + * @def ASSERT + * + * Macro for assertions. + * The argument may or may not be evaluated. + * If the argument is evaluated, it must not evaluate to false. + */ + +/** + * @def ASSERT_EXECUTE + * + * Macro for always-evaluated assertions. + * The argument is evaluated. + * The argument must not evaluate to false. + */ + +/** + * @def DEBUG_ZERO_MEMORY + * + * If debugging is enabled, zeroes the given memory region. + * First argument is pointer to the memory region, second is + * number of bytes. + */ + +/** + * @def WARN_UNUSED + * + * Tells the compiler that the result of a function should not be unused. + * Insert at the end of the declaration of a function before the semicolon. + */ + +/** + * @def B_USE + * + * This can be used to suppress warnings about unused variables. It can + * be applied to a variable or any expression. It does not evaluate the + * expression. + */ + +#ifndef BADVPN_MISC_DEBUG_H +#define BADVPN_MISC_DEBUG_H + +#include +#include +#include +#include +#include + +#define DEBUG(...) \ + { \ + fprintf(stderr, "%s: ", __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } + +#define ASSERT_FORCE(e) \ + { \ + if (!(e)) { \ + fprintf(stderr, "%s:%d Assertion failed\n", __FILE__, __LINE__); \ + abort(); \ + } \ + } + +#ifdef NDEBUG + #define DEBUG_ZERO_MEMORY(buf, len) {} + #define ASSERT(e) {} + #define ASSERT_EXECUTE(e) { (e); } +#else + #define DEBUG_ZERO_MEMORY(buf, len) { memset((buf), 0, (len)); } + #ifdef BADVPN_USE_C_ASSERT + #define ASSERT(e) { assert(e); } + #define ASSERT_EXECUTE(e) \ + { \ + int _assert_res = !!(e); \ + assert(_assert_res); \ + } + #else + #define ASSERT(e) ASSERT_FORCE(e) + #define ASSERT_EXECUTE(e) ASSERT_FORCE(e) + #endif +#endif + +#ifdef __GNUC__ + #define WARN_UNUSED __attribute__((warn_unused_result)) +#else + #define WARN_UNUSED +#endif + +#define B_USE(expr) (void)(sizeof((expr))); + +#define B_ASSERT_USE(expr) { ASSERT(expr) B_USE(expr) } + +#endif diff --git a/app/src/main/jni/badvpn/misc/debugcounter.h b/app/src/main/jni/badvpn/misc/debugcounter.h new file mode 100644 index 0000000..a8a54a1 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/debugcounter.h @@ -0,0 +1,118 @@ +/** + * @file debugcounter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Counter for detecting leaks. + */ + +#ifndef BADVPN_MISC_DEBUGCOUNTER_H +#define BADVPN_MISC_DEBUGCOUNTER_H + +#include + +#include + +/** + * Counter for detecting leaks. + */ +typedef struct { +#ifndef NDEBUG + int32_t c; +#endif +} DebugCounter; + +#ifndef NDEBUG +#define DEBUGCOUNTER_STATIC { 0 } +#else +#define DEBUGCOUNTER_STATIC {} +#endif + +/** + * Initializes the object. + * The object is initialized with counter value zero. + * + * @param obj the object + */ +static void DebugCounter_Init (DebugCounter *obj) +{ +#ifndef NDEBUG + obj->c = 0; +#endif +} + +/** + * Frees the object. + * This does not have to be called when the counter is no longer needed. + * The counter value must be zero. + * + * @param obj the object + */ +static void DebugCounter_Free (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c == 0 || obj->c == INT32_MAX) +#endif +} + +/** + * Increments the counter. + * Increments the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Increment (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c >= 0) + + if (obj->c != INT32_MAX) { + obj->c++; + } +#endif +} + +/** + * Decrements the counter. + * The counter value must be >0. + * Decrements the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Decrement (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c > 0) + + if (obj->c != INT32_MAX) { + obj->c--; + } +#endif +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/debugerror.h b/app/src/main/jni/badvpn/misc/debugerror.h new file mode 100644 index 0000000..182afd7 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/debugerror.h @@ -0,0 +1,90 @@ +/** + * @file debugerror.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mechanism for ensuring an object is destroyed from inside an error handler + * or its jobs. + */ + +#ifndef BADVPN_MISC_DEBUGERROR_H +#define BADVPN_MISC_DEBUGERROR_H + +#include +#include + +#ifndef NDEBUG + #define DEBUGERROR(de, call) \ + { \ + ASSERT(!BPending_IsSet(&(de)->job)) \ + BPending_Set(&(de)->job); \ + (call); \ + } +#else + #define DEBUGERROR(de, call) { (call); } +#endif + +typedef struct { + #ifndef NDEBUG + BPending job; + #endif +} DebugError; + +static void DebugError_Init (DebugError *o, BPendingGroup *pg); +static void DebugError_Free (DebugError *o); +static void DebugError_AssertNoError (DebugError *o); + +#ifndef NDEBUG +static void _DebugError_job_handler (DebugError *o) +{ + ASSERT(0); +} +#endif + +void DebugError_Init (DebugError *o, BPendingGroup *pg) +{ + #ifndef NDEBUG + BPending_Init(&o->job, pg, (BPending_handler)_DebugError_job_handler, o); + #endif +} + +void DebugError_Free (DebugError *o) +{ + #ifndef NDEBUG + BPending_Free(&o->job); + #endif +} + +void DebugError_AssertNoError (DebugError *o) +{ + #ifndef NDEBUG + ASSERT(!BPending_IsSet(&o->job)) + #endif +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/dhcp_proto.h b/app/src/main/jni/badvpn/misc/dhcp_proto.h new file mode 100644 index 0000000..ed83544 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/dhcp_proto.h @@ -0,0 +1,131 @@ +/** + * @file dhcp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the DHCP protocol. + */ + +#ifndef BADVPN_MISC_DHCP_PROTO_H +#define BADVPN_MISC_DHCP_PROTO_H + +#include + +#include + +#define DHCP_OP_BOOTREQUEST 1 +#define DHCP_OP_BOOTREPLY 2 + +#define DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET 1 + +#define DHCP_MAGIC 0x63825363 + +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_END 255 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DOMAIN_NAME_SERVER 6 +#define DHCP_OPTION_HOST_NAME 12 +#define DHCP_OPTION_REQUESTED_IP_ADDRESS 50 +#define DHCP_OPTION_IP_ADDRESS_LEASE_TIME 51 +#define DHCP_OPTION_DHCP_MESSAGE_TYPE 53 +#define DHCP_OPTION_DHCP_SERVER_IDENTIFIER 54 +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 +#define DHCP_OPTION_MAXIMUM_MESSAGE_SIZE 57 +#define DHCP_OPTION_RENEWAL_TIME_VALUE 58 +#define DHCP_OPTION_REBINDING_TIME_VALUE 59 +#define DHCP_OPTION_VENDOR_CLASS_IDENTIFIER 60 +#define DHCP_OPTION_CLIENT_IDENTIFIER 61 + +#define DHCP_MESSAGE_TYPE_DISCOVER 1 +#define DHCP_MESSAGE_TYPE_OFFER 2 +#define DHCP_MESSAGE_TYPE_REQUEST 3 +#define DHCP_MESSAGE_TYPE_DECLINE 4 +#define DHCP_MESSAGE_TYPE_ACK 5 +#define DHCP_MESSAGE_TYPE_NAK 6 +#define DHCP_MESSAGE_TYPE_RELEASE 7 + +B_START_PACKED +struct dhcp_header { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t magic; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_header { + uint8_t type; + uint8_t len; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_dhcp_message_type { + uint8_t type; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_maximum_message_size { + uint16_t size; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_dhcp_server_identifier { + uint32_t id; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_time { + uint32_t time; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_addr { + uint32_t addr; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/misc/ethernet_proto.h b/app/src/main/jni/badvpn/misc/ethernet_proto.h new file mode 100644 index 0000000..6e49e02 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/ethernet_proto.h @@ -0,0 +1,52 @@ +/** + * @file ethernet_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the Ethernet protocol. + */ + +#ifndef BADVPN_MISC_ETHERNET_PROTO_H +#define BADVPN_MISC_ETHERNET_PROTO_H + +#include + +#include + +#define ETHERTYPE_IPV4 0x0800 +#define ETHERTYPE_ARP 0x0806 + +B_START_PACKED +struct ethernet_header { + uint8_t dest[6]; + uint8_t source[6]; + uint16_t type; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/misc/exparray.h b/app/src/main/jni/badvpn/misc/exparray.h new file mode 100644 index 0000000..b24149f --- /dev/null +++ b/app/src/main/jni/badvpn/misc/exparray.h @@ -0,0 +1,101 @@ +/** + * @file exparray.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dynamic array which grows exponentionally on demand. + */ + +#ifndef BADVPN_MISC_EXPARRAY_H +#define BADVPN_MISC_EXPARRAY_H + +#include +#include +#include + +#include + +struct ExpArray { + size_t esize; + size_t size; + void *v; +}; + +static int ExpArray_init (struct ExpArray *o, size_t esize, size_t size) +{ + ASSERT(esize > 0) + ASSERT(size > 0) + + o->esize = esize; + o->size = size; + + if (o->size > SIZE_MAX / o->esize) { + return 0; + } + + if (!(o->v = malloc(o->size * o->esize))) { + return 0; + } + + return 1; +} + +static int ExpArray_resize (struct ExpArray *o, size_t size) +{ + ASSERT(size > 0) + + if (size <= o->size) { + return 1; + } + + size_t newsize = o->size; + + while (newsize < size) { + if (2 > SIZE_MAX / newsize) { + return 0; + } + + newsize = 2 * newsize; + } + + if (newsize > SIZE_MAX / o->esize) { + return 0; + } + + void *newarr = realloc(o->v, newsize * o->esize); + if (!newarr) { + return 0; + } + + o->size = newsize; + o->v = newarr; + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/expstring.h b/app/src/main/jni/badvpn/misc/expstring.h new file mode 100644 index 0000000..3247135 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/expstring.h @@ -0,0 +1,161 @@ +/** + * @file expstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MISC_EXPSTRING_H +#define BADVPN_MISC_EXPSTRING_H + +#include + +#include +#include +#include + +typedef struct { + struct ExpArray arr; + size_t n; +} ExpString; + +static int ExpString_Init (ExpString *c); +static void ExpString_Free (ExpString *c); +static int ExpString_Append (ExpString *c, const char *str); +static int ExpString_AppendChar (ExpString *c, char ch); +static int ExpString_AppendByte (ExpString *c, uint8_t x); +static int ExpString_AppendBinary (ExpString *c, const uint8_t *data, size_t len); +static int ExpString_AppendZeros (ExpString *c, size_t len); +static char * ExpString_Get (ExpString *c); +static size_t ExpString_Length (ExpString *c); + +int ExpString_Init (ExpString *c) +{ + if (!ExpArray_init(&c->arr, 1, 16)) { + return 0; + } + + c->n = 0; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +void ExpString_Free (ExpString *c) +{ + free(c->arr.v); +} + +int ExpString_Append (ExpString *c, const char *str) +{ + ASSERT(str) + + size_t l = strlen(str); + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(l), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memcpy((char *)c->arr.v + c->n, str, l); + c->n += l; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendChar (ExpString *c, char ch) +{ + ASSERT(ch != '\0') + + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_fromint(2)); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + ((char *)c->arr.v)[c->n] = ch; + c->n++; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendByte (ExpString *c, uint8_t x) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_fromint(2)); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + ((uint8_t *)c->arr.v)[c->n] = x; + c->n++; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendBinary (ExpString *c, const uint8_t *data, size_t len) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(len), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memcpy((char *)c->arr.v + c->n, data, len); + c->n += len; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendZeros (ExpString *c, size_t len) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(len), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memset((char *)c->arr.v + c->n, 0, len); + c->n += len; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +char * ExpString_Get (ExpString *c) +{ + return (char *)c->arr.v; +} + +size_t ExpString_Length (ExpString *c) +{ + return c->n; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/find_char.h b/app/src/main/jni/badvpn/misc/find_char.h new file mode 100644 index 0000000..9277ac6 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/find_char.h @@ -0,0 +1,58 @@ +/** + * @file find_char.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_FIND_CHAR_H +#define BADVPN_FIND_CHAR_H + +#include + +#include + +/** + * Finds the first character 'c' in the string represented by 'str' and 'len'. + * If found, returns 1 and writes the position to *out_pos (if out_pos!=NULL). + * If not found, returns 0 and does not modify *out_pos. + */ +static int b_find_char_bin (const char *str, size_t len, char c, size_t *out_pos) +{ + ASSERT(str) + + for (size_t i = 0; i < len; i++) { + if (str[i] == c) { + if (out_pos) { + *out_pos = i; + } + return 1; + } + } + + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/find_program.h b/app/src/main/jni/badvpn/misc/find_program.h new file mode 100644 index 0000000..7c4db09 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/find_program.h @@ -0,0 +1,69 @@ +/** + * @file find_program.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function that finds the absolute path of a program by checking a predefined + * list of directories. + */ + +#ifndef BADVPN_FIND_PROGRAM_H +#define BADVPN_FIND_PROGRAM_H + +#include +#include + +#include +#include + +static char * badvpn_find_program (const char *name); + +static char * badvpn_find_program (const char *name) +{ + ASSERT(name) + + const char *dirs[] = {"/usr/sbin", "/usr/bin", "/sbin", "/bin", NULL}; + + for (size_t i = 0; dirs[i]; i++) { + char *path = concat_strings(3, dirs[i], "/", name); + if (!path) { + goto fail; + } + + if (access(path, X_OK) == 0) { + return path; + } + + free(path); + } + +fail: + return NULL; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/get_iface_info.h b/app/src/main/jni/badvpn/misc/get_iface_info.h new file mode 100644 index 0000000..190741b --- /dev/null +++ b/app/src/main/jni/badvpn/misc/get_iface_info.h @@ -0,0 +1,110 @@ +/** + * @file get_iface_info.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_GETIFACEINFO_H +#define BADVPN_GETIFACEINFO_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Returns information about a network interface with the given name. + * + * @param ifname name of interface to get information for + * @param out_mac the MAC address will be returned here, unless NULL + * @param out_mtu the MTU will be returned here, unless NULL + * @param out_ifindex the interface index will be returned here, unless NULL + * @return 1 on success, 0 on failure + */ +static int badvpn_get_iface_info (const char *ifname, uint8_t *out_mac, int *out_mtu, int *out_ifindex) WARN_UNUSED; + + +static int badvpn_get_iface_info (const char *ifname, uint8_t *out_mac, int *out_mtu, int *out_ifindex) +{ + ASSERT(ifname) + + struct ifreq ifr; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + goto fail0; + } + + // get MAC + if (out_mac) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFHWADDR, &ifr)) { + goto fail1; + } + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + goto fail1; + } + memcpy(out_mac, ifr.ifr_hwaddr.sa_data, 6); + } + + // get MTU + if (out_mtu) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFMTU, &ifr)) { + goto fail1; + } + *out_mtu = ifr.ifr_mtu; + } + + // get interface index + if (out_ifindex) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFINDEX, &ifr)) { + goto fail1; + } + *out_ifindex = ifr.ifr_ifindex; + } + + close(s); + + return 1; + +fail1: + close(s); +fail0: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/grow_array.h b/app/src/main/jni/badvpn/misc/grow_array.h new file mode 100644 index 0000000..9a42d04 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/grow_array.h @@ -0,0 +1,139 @@ +/** + * @file grow_array.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// GROWARRAY_NAME - prefix of of functions to define +// GROWARRAY_OBJECT_TYPE - type of structure where array and capacity sizes are +// GROWARRAY_ARRAY_MEMBER - array member +// GROWARRAY_CAPACITY_MEMBER - capacity member +// GROWARRAY_MAX_CAPACITY - max value of capacity member + +#include +#include +#include + +#include +#include +#include + +#define GrowArrayObject GROWARRAY_OBJECT_TYPE +#define GrowArray_Init MERGE(GROWARRAY_NAME, _Init) +#define GrowArray_InitEmpty MERGE(GROWARRAY_NAME, _InitEmpty) +#define GrowArray_Free MERGE(GROWARRAY_NAME, _Free) +#define GrowArray_DoubleUp MERGE(GROWARRAY_NAME, _DoubleUp) +#define GrowArray_DoubleUpLimit MERGE(GROWARRAY_NAME, _DoubleUpLimit) + +static int GrowArray_Init (GrowArrayObject *o, size_t capacity) WARN_UNUSED; +static void GrowArray_InitEmpty (GrowArrayObject *o); +static void GrowArray_Free (GrowArrayObject *o); +static int GrowArray_DoubleUp (GrowArrayObject *o) WARN_UNUSED; +static int GrowArray_DoubleUpLimit (GrowArrayObject *o, size_t limit) WARN_UNUSED; + +static int GrowArray_Init (GrowArrayObject *o, size_t capacity) +{ + if (capacity > GROWARRAY_MAX_CAPACITY) { + return 0; + } + + if (capacity == 0) { + o->GROWARRAY_ARRAY_MEMBER = NULL; + } else { + if (!(o->GROWARRAY_ARRAY_MEMBER = BAllocArray(capacity, sizeof(o->GROWARRAY_ARRAY_MEMBER[0])))) { + return 0; + } + } + + o->GROWARRAY_CAPACITY_MEMBER = capacity; + + return 1; +} + +static void GrowArray_InitEmpty (GrowArrayObject *o) +{ + o->GROWARRAY_ARRAY_MEMBER = NULL; + o->GROWARRAY_CAPACITY_MEMBER = 0; +} + +static void GrowArray_Free (GrowArrayObject *o) +{ + if (o->GROWARRAY_ARRAY_MEMBER) { + BFree(o->GROWARRAY_ARRAY_MEMBER); + } +} + +static int GrowArray_DoubleUp (GrowArrayObject *o) +{ + return GrowArray_DoubleUpLimit(o, SIZE_MAX); +} + +static int GrowArray_DoubleUpLimit (GrowArrayObject *o, size_t limit) +{ + if (o->GROWARRAY_CAPACITY_MEMBER > SIZE_MAX / 2 || o->GROWARRAY_CAPACITY_MEMBER > GROWARRAY_MAX_CAPACITY / 2) { + return 0; + } + + size_t newcap = 2 * o->GROWARRAY_CAPACITY_MEMBER; + if (newcap == 0) { + newcap = 1; + } + + if (newcap > limit) { + newcap = limit; + if (newcap == o->GROWARRAY_CAPACITY_MEMBER) { + return 0; + } + } + + void *newarr = BAllocArray(newcap, sizeof(o->GROWARRAY_ARRAY_MEMBER[0])); + if (!newarr) { + return 0; + } + + memcpy(newarr, o->GROWARRAY_ARRAY_MEMBER, o->GROWARRAY_CAPACITY_MEMBER * sizeof(o->GROWARRAY_ARRAY_MEMBER[0])); + + BFree(o->GROWARRAY_ARRAY_MEMBER); + + o->GROWARRAY_ARRAY_MEMBER = newarr; + o->GROWARRAY_CAPACITY_MEMBER = newcap; + + return 1; +} + +#undef GROWARRAY_NAME +#undef GROWARRAY_OBJECT_TYPE +#undef GROWARRAY_ARRAY_MEMBER +#undef GROWARRAY_CAPACITY_MEMBER +#undef GROWARRAY_MAX_CAPACITY + +#undef GrowArrayObject +#undef GrowArray_Init +#undef GrowArray_InitEmpty +#undef GrowArray_Free +#undef GrowArray_DoubleUp +#undef GrowArray_DoubleUpLimit diff --git a/app/src/main/jni/badvpn/misc/hashfun.h b/app/src/main/jni/badvpn/misc/hashfun.h new file mode 100644 index 0000000..5e8956a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/hashfun.h @@ -0,0 +1,60 @@ +/** + * @file hashfun.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_HASHFUN_H +#define BADVPN_HASHFUN_H + +#include +#include + +static size_t badvpn_djb2_hash (const uint8_t *str) +{ + size_t hash = 5381; + int c; + + while (c = *str++) { + hash = ((hash << 5) + hash) + c; + } + + return hash; +} + +static size_t badvpn_djb2_hash_bin (const uint8_t *str, size_t str_len) +{ + size_t hash = 5381; + + while (str_len-- > 0) { + int c = *str++; + hash = ((hash << 5) + hash) + c; + } + + return hash; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/igmp_proto.h b/app/src/main/jni/badvpn/misc/igmp_proto.h new file mode 100644 index 0000000..9188ea0 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/igmp_proto.h @@ -0,0 +1,97 @@ +/** + * @file igmp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the IGMP protocol. + */ + +#ifndef BADVPN_MISC_IGMP_PROTO_H +#define BADVPN_MISC_IGMP_PROTO_H + +#include + +#include + +#define IGMP_TYPE_MEMBERSHIP_QUERY 0x11 +#define IGMP_TYPE_V1_MEMBERSHIP_REPORT 0x12 +#define IGMP_TYPE_V2_MEMBERSHIP_REPORT 0x16 +#define IGMP_TYPE_V3_MEMBERSHIP_REPORT 0x22 +#define IGMP_TYPE_V2_LEAVE_GROUP 0x17 + +#define IGMP_RECORD_TYPE_MODE_IS_INCLUDE 1 +#define IGMP_RECORD_TYPE_MODE_IS_EXCLUDE 2 +#define IGMP_RECORD_TYPE_CHANGE_TO_INCLUDE_MODE 3 +#define IGMP_RECORD_TYPE_CHANGE_TO_EXCLUDE_MODE 4 + +B_START_PACKED +struct igmp_source { + uint32_t addr; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_base { + uint8_t type; + uint8_t max_resp_code; + uint16_t checksum; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_query_extra { + uint32_t group; + uint8_t reserved4_suppress1_qrv3; + uint8_t qqic; + uint16_t number_of_sources; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_report_extra { + uint16_t reserved; + uint16_t number_of_group_records; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_report_record { + uint8_t type; + uint8_t aux_data_len; + uint16_t number_of_sources; + uint32_t group; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v2_extra { + uint32_t group; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/misc/ipaddr.h b/app/src/main/jni/badvpn/misc/ipaddr.h new file mode 100644 index 0000000..8c7cb05 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/ipaddr.h @@ -0,0 +1,218 @@ +/** + * @file ipaddr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IP address parsing functions. + */ + +#ifndef BADVPN_MISC_IPADDR_H +#define BADVPN_MISC_IPADDR_H + +#include +#include + +#include +#include +#include +#include +#include + +struct ipv4_ifaddr { + uint32_t addr; + int prefix; +}; + +static int ipaddr_parse_ipv4_addr_bin (const char *name, size_t name_len, uint32_t *out_addr); +static int ipaddr_parse_ipv4_addr (const char *name, uint32_t *out_addr); +static int ipaddr_parse_ipv4_prefix_bin (const char *str, size_t str_len, int *num); +static int ipaddr_parse_ipv4_prefix (const char *str, int *num); +static int ipaddr_parse_ipv4_ifaddr_bin (const char *str, size_t str_len, struct ipv4_ifaddr *out); +static int ipaddr_parse_ipv4_ifaddr (const char *str, struct ipv4_ifaddr *out); +static int ipaddr_ipv4_ifaddr_from_addr_mask (uint32_t addr, uint32_t mask, struct ipv4_ifaddr *out); +static uint32_t ipaddr_ipv4_mask_from_prefix (int prefix); +static int ipaddr_ipv4_prefix_from_mask (uint32_t mask, int *out_prefix); +static int ipaddr_ipv4_addrs_in_network (uint32_t addr1, uint32_t addr2, int netprefix); + +#define IPADDR_PRINT_MAX 19 + +static void ipaddr_print_addr (uint32_t addr, char *out); +static void ipaddr_print_ifaddr (struct ipv4_ifaddr ifaddr, char *out); + +int ipaddr_parse_ipv4_addr_bin (const char *name, size_t name_len, uint32_t *out_addr) +{ + for (size_t i = 0; ; i++) { + size_t j; + for (j = 0; j < name_len && name[j] != '.'; j++); + + if ((j == name_len && i < 3) || (j < name_len && i == 3)) { + return 0; + } + + if (j < 1 || j > 3) { + return 0; + } + + uintmax_t d; + if (!parse_unsigned_integer_bin(name, j, &d)) { + return 0; + } + + if (d > 255) { + return 0; + } + + ((uint8_t *)out_addr)[i] = d; + + if (i == 3) { + return 1; + } + + name += j + 1; + name_len -= j + 1; + } +} + +int ipaddr_parse_ipv4_addr (const char *name, uint32_t *out_addr) +{ + return ipaddr_parse_ipv4_addr_bin(name, strlen(name), out_addr); +} + +int ipaddr_parse_ipv4_prefix_bin (const char *str, size_t str_len, int *num) +{ + uintmax_t d; + if (!parse_unsigned_integer_bin(str, str_len, &d)) { + return 0; + } + if (d > 32) { + return 0; + } + + *num = d; + return 1; +} + +int ipaddr_parse_ipv4_prefix (const char *str, int *num) +{ + return ipaddr_parse_ipv4_prefix_bin(str, strlen(str), num); +} + +int ipaddr_parse_ipv4_ifaddr_bin (const char *str, size_t str_len, struct ipv4_ifaddr *out) +{ + size_t slash_pos; + if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { + return 0; + } + + return (ipaddr_parse_ipv4_addr_bin(str, slash_pos, &out->addr) && + ipaddr_parse_ipv4_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); +} + +int ipaddr_parse_ipv4_ifaddr (const char *str, struct ipv4_ifaddr *out) +{ + return ipaddr_parse_ipv4_ifaddr_bin(str, strlen(str), out); +} + +int ipaddr_ipv4_ifaddr_from_addr_mask (uint32_t addr, uint32_t mask, struct ipv4_ifaddr *out) +{ + int prefix; + if (!ipaddr_ipv4_prefix_from_mask(mask, &prefix)) { + return 0; + } + + out->addr = addr; + out->prefix = prefix; + return 1; +} + +uint32_t ipaddr_ipv4_mask_from_prefix (int prefix) +{ + ASSERT(prefix >= 0) + ASSERT(prefix <= 32) + + uint32_t t = 0; + for (int i = 0; i < prefix; i++) { + t |= 1 << (32 - i - 1); + } + + return hton32(t); +} + +int ipaddr_ipv4_prefix_from_mask (uint32_t mask, int *out_prefix) +{ + uint32_t t = 0; + int i; + for (i = 0; i <= 32; i++) { + if (ntoh32(mask) == t) { + break; + } + if (i < 32) { + t |= (1 << (32 - i - 1)); + } + } + if (!(i <= 32)) { + return 0; + } + + *out_prefix = i; + return 1; +} + +int ipaddr_ipv4_addrs_in_network (uint32_t addr1, uint32_t addr2, int netprefix) +{ + ASSERT(netprefix >= 0) + ASSERT(netprefix <= 32) + + uint32_t mask = ipaddr_ipv4_mask_from_prefix(netprefix); + + return !!((addr1 & mask) == (addr2 & mask)); +} + +void ipaddr_print_addr (uint32_t addr, char *out) +{ + ASSERT(out) + + uint8_t *b = (uint8_t *)&addr; + + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, + b[0], b[1], b[2], b[3]); +} + +void ipaddr_print_ifaddr (struct ipv4_ifaddr ifaddr, char *out) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + ASSERT(out) + + uint8_t *b = (uint8_t *)&ifaddr.addr; + + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d", + b[0], b[1], b[2], b[3], ifaddr.prefix); +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/ipaddr6.h b/app/src/main/jni/badvpn/misc/ipaddr6.h new file mode 100644 index 0000000..dc48fa4 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/ipaddr6.h @@ -0,0 +1,400 @@ +/** + * @file ipaddr6.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 address parsing functions. + */ + +#ifndef BADVPN_MISC_IPADDR6_H +#define BADVPN_MISC_IPADDR6_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct ipv6_addr { + uint8_t bytes[16]; +}; + +struct ipv6_ifaddr { + struct ipv6_addr addr; + int prefix; +}; + +static int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num); +static int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num); +static int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out); +static int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out); +static int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out); +static void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask); +static int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix); +static int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix); + +#define IPADDR6_PRINT_MAX 44 + +static void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf); +static void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf); + +int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr) +{ + int num_blocks = 0; + int compress_pos = -1; + uint16_t block = 0; + int empty = 1; + + size_t i = 0; + + while (i < name_len) { + if (name[i] == '.') { + goto ipv4_ending; + } else if (name[i] == ':') { + int is_double = (i + 1 < name_len && name[i + 1] == ':'); + + if (i > 0) { + if (empty || num_blocks == 7) { + return 0; + } + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + block = 0; + empty = 1; + } + else if (!is_double) { + return 0; + } + + if (is_double) { + if (compress_pos != -1) { + return 0; + } + compress_pos = num_blocks; + } + + i += 1 + is_double; + } else { + int digit = decode_hex_digit(name[i]); + if (digit < 0) { + return 0; + } + if (block > UINT16_MAX / 16) { + return 0; + } + block *= 16; + if (digit > UINT16_MAX - block) { + return 0; + } + block += digit; + empty = 0; + i += 1; + } + } + + if (!empty) { + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + } + else if (num_blocks != compress_pos) { + return 0; + } + +ipv4_done: + if (compress_pos == -1) { + if (num_blocks != 8) { + return 0; + } + compress_pos = 0; + } + + int num_rear = num_blocks - compress_pos; + memmove(out_addr->bytes + 2 * (8 - num_rear), out_addr->bytes + 2 * compress_pos, 2 * num_rear); + memset(out_addr->bytes + 2 * compress_pos, 0, 2 * (8 - num_rear - compress_pos)); + + return 1; + +ipv4_ending: + if (empty || (num_blocks == 0 && compress_pos == -1)) { + return 0; + } + + while (name[i - 1] != ':') { + i--; + } + + uint8_t bytes[4]; + int cur_byte = 0; + uint8_t byte = 0; + empty = 1; + + while (i < name_len) { + if (name[i] == '.') { + if (empty || cur_byte == 3) { + return 0; + } + bytes[cur_byte] = byte; + cur_byte++; + byte = 0; + empty = 1; + } else { + if (!empty && byte == 0) { + return 0; + } + int digit = decode_decimal_digit(name[i]); + if (digit < 0) { + return 0; + } + if (byte > UINT8_MAX / 10) { + return 0; + } + byte *= 10; + if (digit > UINT8_MAX - byte) { + return 0; + } + byte += digit; + empty = 0; + } + i++; + } + + if (cur_byte != 3 || empty) { + return 0; + } + bytes[cur_byte] = byte; + + if (8 - num_blocks < 2) { + return 0; + } + memcpy(out_addr->bytes + 2 * num_blocks, bytes, 4); + num_blocks += 2; + + goto ipv4_done; +} + +int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr) +{ + return ipaddr6_parse_ipv6_addr_bin(name, strlen(name), out_addr); +} + +int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num) +{ + uintmax_t d; + if (!parse_unsigned_integer_bin(str, str_len, &d)) { + return 0; + } + if (d > 128) { + return 0; + } + + *out_num = d; + return 1; +} + +int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num) +{ + return ipaddr6_parse_ipv6_prefix_bin(str, strlen(str), out_num); +} + +int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out) +{ + size_t slash_pos; + if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { + return 0; + } + + return (ipaddr6_parse_ipv6_addr_bin(str, slash_pos, &out->addr) && + ipaddr6_parse_ipv6_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); +} + +int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out) +{ + return ipaddr6_parse_ipv6_ifaddr_bin(str, strlen(str), out); +} + +int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out) +{ + int prefix; + if (!ipaddr6_ipv6_prefix_from_mask(mask, &prefix)) { + return 0; + } + + out->addr = addr; + out->prefix = prefix; + return 1; +} + +void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask) +{ + ASSERT(prefix >= 0) + ASSERT(prefix <= 128) + + int quot = prefix / 8; + int rem = prefix % 8; + + if (quot > 0) { + memset(out_mask->bytes, UINT8_MAX, quot); + } + if (16 - quot > 0) { + memset(out_mask->bytes + quot, 0, 16 - quot); + } + + for (int i = 0; i < rem; i++) { + out_mask->bytes[quot] |= (uint8_t)1 << (8 - i - 1); + } +} + +int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix) +{ + int prefix = 0; + int i = 0; + + while (i < 16 && mask.bytes[i] == UINT8_MAX) { + prefix += 8; + i++; + } + + if (i < 16) { + uint8_t t = 0; + int j; + for (j = 0; j <= 8; j++) { + if (mask.bytes[i] == t) { + break; + } + if (j < 8) { + t |= ((uint8_t)1 << (8 - j - 1)); + } + } + if (!(j <= 8)) { + return 0; + } + + prefix += j; + i++; + + while (i < 16) { + if (mask.bytes[i] != 0) { + return 0; + } + i++; + } + } + + *out_prefix = prefix; + return 1; +} + +int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix) +{ + ASSERT(netprefix >= 0) + ASSERT(netprefix <= 128) + + int quot = netprefix / 8; + int rem = netprefix % 8; + + if (memcmp(addr1.bytes, addr2.bytes, quot)) { + return 0; + } + + if (rem == 0) { + return 1; + } + + uint8_t t = 0; + for (int i = 0; i < rem; i++) { + t |= (uint8_t)1 << (8 - i - 1); + } + + return ((addr1.bytes[quot] & t) == (addr2.bytes[quot] & t)); +} + +void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf) +{ + int largest_start = 0; + int largest_len = 0; + int current_start = 0; + int current_len = 0; + + for (int i = 0; i < 8; i++) { + if (addr.bytes[2 * i] == 0 && addr.bytes[2 * i + 1] == 0) { + current_len++; + if (current_len > largest_len) { + largest_start = current_start; + largest_len = current_len; + } + } else { + current_start = i + 1; + current_len = 0; + } + } + + if (largest_len > 1) { + for (int i = 0; i < largest_start; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%"PRIx16":", block); + } + if (largest_start == 0) { + out_buf += sprintf(out_buf, ":"); + } + + for (int i = largest_start + largest_len; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, ":%"PRIx16, block); + } + if (largest_start + largest_len == 8) { + out_buf += sprintf(out_buf, ":"); + } + } else { + const char *prefix = ""; + for (int i = 0; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%s%"PRIx16, prefix, block); + prefix = ":"; + } + } +} + +void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf) +{ + ASSERT(addr.prefix >= 0) + ASSERT(addr.prefix <= 128) + + ipaddr6_print_addr(addr.addr, out_buf); + sprintf(out_buf + strlen(out_buf), "/%d", addr.prefix); +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/ipv4_proto.h b/app/src/main/jni/badvpn/misc/ipv4_proto.h new file mode 100644 index 0000000..fea7260 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/ipv4_proto.h @@ -0,0 +1,145 @@ +/** + * @file ipv4_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the IPv4 protocol. + */ + +#ifndef BADVPN_MISC_IPV4_PROTO_H +#define BADVPN_MISC_IPV4_PROTO_H + +#include +#include + +#include +#include +#include +#include + +#define IPV4_PROTOCOL_IGMP 2 +#define IPV4_PROTOCOL_UDP 17 + +B_START_PACKED +struct ipv4_header { + uint8_t version4_ihl4; + uint8_t ds; + uint16_t total_length; + // + uint16_t identification; + uint16_t flags3_fragmentoffset13; + // + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + // + uint32_t source_address; + // + uint32_t destination_address; +} B_PACKED; +B_END_PACKED + +#define IPV4_GET_VERSION(_header) (((_header).version4_ihl4&0xF0)>>4) +#define IPV4_GET_IHL(_header) (((_header).version4_ihl4&0x0F)>>0) + +#define IPV4_MAKE_VERSION_IHL(size) (((size)/4) + (4 << 4)) + +static uint16_t ipv4_checksum (const struct ipv4_header *header, const char *extra, uint16_t extra_len) +{ + ASSERT(extra_len % 2 == 0) + ASSERT(extra_len == 0 || extra) + + uint32_t t = 0; + + for (uint16_t i = 0; i < sizeof(*header) / 2; i++) { + t += badvpn_read_be16((const char *)header + 2 * i); + } + + for (uint16_t i = 0; i < extra_len / 2; i++) { + t += badvpn_read_be16((const char *)extra + 2 * i); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + return hton16(~t); +} + +static int ipv4_check (uint8_t *data, int data_len, struct ipv4_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv4_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if (IPV4_GET_VERSION(*out_header) != 4) { + return 0; + } + + // check options + uint16_t header_len = IPV4_GET_IHL(*out_header) * 4; + if (header_len < sizeof(struct ipv4_header)) { + return 0; + } + if (header_len > data_len) { + return 0; + } + + // check total length + uint16_t total_length = ntoh16(out_header->total_length); + if (total_length < header_len) { + return 0; + } + if (total_length > data_len) { + return 0; + } + + // check checksum + uint16_t checksum_in_packet = out_header->checksum; + out_header->checksum = hton16(0); + uint16_t checksum_computed = ipv4_checksum(out_header, (char *)data + sizeof(*out_header), header_len - sizeof(*out_header)); + out_header->checksum = checksum_in_packet; + if (checksum_in_packet != checksum_computed) { + return 0; + } + + *out_payload = data + header_len; + *out_payload_len = total_length - header_len; + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/ipv6_proto.h b/app/src/main/jni/badvpn/misc/ipv6_proto.h new file mode 100644 index 0000000..b255541 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/ipv6_proto.h @@ -0,0 +1,86 @@ +/** + * @file ipv6_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_IPV6_PROTO_H +#define BADVPN_IPV6_PROTO_H + +#include +#include + +#include +#include +#include + +#define IPV6_NEXT_IGMP 2 +#define IPV6_NEXT_UDP 17 + +B_START_PACKED +struct ipv6_header { + uint8_t version4_tc4; + uint8_t tc4_fl4; + uint16_t fl; + uint16_t payload_length; + uint8_t next_header; + uint8_t hop_limit; + uint8_t source_address[16]; + uint8_t destination_address[16]; +} B_PACKED; +B_END_PACKED + +static int ipv6_check (uint8_t *data, int data_len, struct ipv6_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv6_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if ((ntoh8(out_header->version4_tc4) >> 4) != 6) { + return 0; + } + + // check payload length + uint16_t payload_length = ntoh16(out_header->payload_length); + if (payload_length > data_len - sizeof(struct ipv6_header)) { + return 0; + } + + *out_payload = data + sizeof(struct ipv6_header); + *out_payload_len = payload_length; + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/loggers_string.h b/app/src/main/jni/badvpn/misc/loggers_string.h new file mode 100644 index 0000000..66986b8 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/loggers_string.h @@ -0,0 +1,43 @@ +/** + * @file loggers_string.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * List of available loggers. + */ + +#ifndef BADVPN_MISC_LOGGERSSTRING_H +#define BADVPN_MISC_LOGGERSSTRING_H + +#ifdef BADVPN_USE_WINAPI +#define LOGGERS_STRING "stdout" +#else +#define LOGGERS_STRING "stdout/syslog" +#endif + +#endif diff --git a/app/src/main/jni/badvpn/misc/loglevel.h b/app/src/main/jni/badvpn/misc/loglevel.h new file mode 100644 index 0000000..6e9f911 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/loglevel.h @@ -0,0 +1,80 @@ +/** + * @file loglevel.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Log level specification parsing function. + */ + +#ifndef BADVPN_MISC_LOGLEVEL_H +#define BADVPN_MISC_LOGLEVEL_H + +#include + +#include + +/** + * Parses the log level string. + * + * @param str log level string. Recognizes none, error, warning, notice, + * info, debug. + * @return 0 for none, one of BLOG_* for some log level, -1 for unrecognized + */ +static int parse_loglevel (char *str); + +int parse_loglevel (char *str) +{ + if (!strcmp(str, "none")) { + return 0; + } + if (!strcmp(str, "error")) { + return BLOG_ERROR; + } + if (!strcmp(str, "warning")) { + return BLOG_WARNING; + } + if (!strcmp(str, "notice")) { + return BLOG_NOTICE; + } + if (!strcmp(str, "info")) { + return BLOG_INFO; + } + if (!strcmp(str, "debug")) { + return BLOG_DEBUG; + } + + char *endptr; + long int res = strtol(str, &endptr, 10); + if (*str && !*endptr && res >= 0 && res <= BLOG_DEBUG) { + return res; + } + + return -1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/maxalign.h b/app/src/main/jni/badvpn/misc/maxalign.h new file mode 100644 index 0000000..cb1f460 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/maxalign.h @@ -0,0 +1,53 @@ +/** + * @file maxalign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MAXALIGN_H +#define BADVPN_MAXALIGN_H + +#include +#include + +typedef union { + short a; + long b; + long long c; + double d; + long double e; + void *f; + uint8_t g; + uint16_t h; + uint32_t i; + uint64_t j; + size_t k; + void (*l) (void); +} bmax_align_t; + +#define BMAX_ALIGN (__alignof(bmax_align_t)) + +#endif diff --git a/app/src/main/jni/badvpn/misc/merge.h b/app/src/main/jni/badvpn/misc/merge.h new file mode 100644 index 0000000..5322771 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/merge.h @@ -0,0 +1,36 @@ +/** + * @file merge.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MERGE_H +#define BADVPN_MERGE_H + +#define MERGE_HELPER(x, y) x ## y +#define MERGE(x, y) MERGE_HELPER(x, y) + +#endif diff --git a/app/src/main/jni/badvpn/misc/minmax.h b/app/src/main/jni/badvpn/misc/minmax.h new file mode 100644 index 0000000..ca82055 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/minmax.h @@ -0,0 +1,56 @@ +/** + * @file minmax.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Minimum and maximum macros. + */ + +#ifndef BADVPN_MISC_MINMAX_H +#define BADVPN_MISC_MINMAX_H + +#include +#include + +#define DEFINE_BMINMAX(name, type) \ +static type bmin ## name (type a, type b) { return (a < b ? a : b); } \ +static type bmax ## name (type a, type b) { return (a > b ? a : b); } + +DEFINE_BMINMAX(_size, size_t) +DEFINE_BMINMAX(_int, int) +DEFINE_BMINMAX(_int8, int8_t) +DEFINE_BMINMAX(_int16, int16_t) +DEFINE_BMINMAX(_int32, int32_t) +DEFINE_BMINMAX(_int64, int64_t) +DEFINE_BMINMAX(_uint, unsigned int) +DEFINE_BMINMAX(_uint8, uint8_t) +DEFINE_BMINMAX(_uint16, uint16_t) +DEFINE_BMINMAX(_uint32, uint32_t) +DEFINE_BMINMAX(_uint64, uint64_t) + +#endif diff --git a/app/src/main/jni/badvpn/misc/modadd.h b/app/src/main/jni/badvpn/misc/modadd.h new file mode 100644 index 0000000..4e4f04a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/modadd.h @@ -0,0 +1,59 @@ +/** + * @file modadd.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Modular addition macro. + * + * Calculates (x + y) mod m, assuming + * 0 <= x < m and 0 <= y < m. + */ + +#ifndef BADVPN_MISC_MODADD_H +#define BADVPN_MISC_MODADD_H + +#include + +#define DECLARE_BMODADD(type, name) \ +static type bmodadd_##name (type x, type y, type m) \ +{ \ + ASSERT(x >= 0) \ + ASSERT(x < m) \ + ASSERT(y >= 0) \ + ASSERT(y < m) \ + \ + if (y >= m - x) { \ + return (y - (m - x)); \ + } else { \ + return (x + y); \ + } \ +} \ + +DECLARE_BMODADD(int, int) + +#endif diff --git a/app/src/main/jni/badvpn/misc/mswsock.h b/app/src/main/jni/badvpn/misc/mswsock.h new file mode 100644 index 0000000..4f4c38d --- /dev/null +++ b/app/src/main/jni/badvpn/misc/mswsock.h @@ -0,0 +1,229 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include + +#ifndef _MSWSOCK_ +#define _MSWSOCK_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SO_CONNDATA 0x7000 +#define SO_CONNOPT 0x7001 +#define SO_DISCDATA 0x7002 +#define SO_DISCOPT 0x7003 +#define SO_CONNDATALEN 0x7004 +#define SO_CONNOPTLEN 0x7005 +#define SO_DISCDATALEN 0x7006 +#define SO_DISCOPTLEN 0x7007 + +#define SO_OPENTYPE 0x7008 + +#define SO_SYNCHRONOUS_ALERT 0x10 +#define SO_SYNCHRONOUS_NONALERT 0x20 + +#define SO_MAXDG 0x7009 +#define SO_MAXPATHDG 0x700A +#define SO_UPDATE_ACCEPT_CONTEXT 0x700B +#define SO_CONNECT_TIME 0x700C +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 + +#define TCP_BSDURGENT 0x7000 + +#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) +#if (_WIN32_WINNT < 0x0600) && (_WIN32_WINNT >= 0x0501) +#define SIO_SOCKET_CLOSE_NOTIFY _WSAIOW(IOC_VENDOR,13) +#endif /* >= XP && < VISTA */ +#if (_WIN32_WINNT >= 0x0600) +#define SIO_BSP_HANDLE _WSAIOR(IOC_WS2,27) +#define SIO_BSP_HANDLE_SELECT _WSAIOR(IOC_WS2,28) +#define SIO_BSP_HANDLE_POLL _WSAIOR(IOC_WS2,29) + +#define SIO_EXT_SELECT _WSAIORW(IOC_WS2,30) +#define SIO_EXT_POLL _WSAIORW(IOC_WS2,31) +#define SIO_EXT_SENDMSG _WSAIORW(IOC_WS2,32) + +#define SIO_BASE_HANDLE _WSAIOR(IOC_WS2,34) +#endif /* _WIN32_WINNT >= 0x0600 */ + +#ifndef __MSWSOCK_WS1_SHARED + int WINAPI WSARecvEx(SOCKET s,char *buf,int len,int *flags); +#endif /* __MSWSOCK_WS1_SHARED */ + +#define TF_DISCONNECT 0x01 +#define TF_REUSE_SOCKET 0x02 +#define TF_WRITE_BEHIND 0x04 +#define TF_USE_DEFAULT_WORKER 0x00 +#define TF_USE_SYSTEM_THREAD 0x10 +#define TF_USE_KERNEL_APC 0x20 + +#include +#ifndef __MSWSOCK_WS1_SHARED + WINBOOL WINAPI TransmitFile(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + WINBOOL WINAPI AcceptEx(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + VOID WINAPI GetAcceptExSockaddrs(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); +#endif /* __MSWSOCK_WS1_SHARED */ + + typedef WINBOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + +#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef WINBOOL (WINAPI *LPFN_ACCEPTEX)(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + +#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef VOID (WINAPI *LPFN_GETACCEPTEXSOCKADDRS)(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); + +#define WSAID_GETACCEPTEXSOCKADDRS {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef struct _TRANSMIT_PACKETS_ELEMENT { + ULONG dwElFlags; +#define TP_ELEMENT_MEMORY 1 +#define TP_ELEMENT_FILE 2 +#define TP_ELEMENT_EOP 4 + ULONG cLength; + __MINGW_EXTENSION union { + __MINGW_EXTENSION struct { + LARGE_INTEGER nFileOffset; + HANDLE hFile; + }; + PVOID pBuffer; + }; + } TRANSMIT_PACKETS_ELEMENT,*PTRANSMIT_PACKETS_ELEMENT,*LPTRANSMIT_PACKETS_ELEMENT; + +#define TP_DISCONNECT TF_DISCONNECT +#define TP_REUSE_SOCKET TF_REUSE_SOCKET +#define TP_USE_DEFAULT_WORKER TF_USE_DEFAULT_WORKER +#define TP_USE_SYSTEM_THREAD TF_USE_SYSTEM_THREAD +#define TP_USE_KERNEL_APC TF_USE_KERNEL_APC + + typedef WINBOOL (WINAPI *LPFN_TRANSMITPACKETS) (SOCKET hSocket,LPTRANSMIT_PACKETS_ELEMENT lpPacketArray,DWORD nElementCount,DWORD nSendSize,LPOVERLAPPED lpOverlapped,DWORD dwFlags); + +#define WSAID_TRANSMITPACKETS {0xd9689da0,0x1f90,0x11d3,{0x99,0x71,0x00,0xc0,0x4f,0x68,0xc8,0x76}} + + typedef WINBOOL (WINAPI *LPFN_CONNECTEX)(SOCKET s,const struct sockaddr *name,int namelen,PVOID lpSendBuffer,DWORD dwSendDataLength,LPDWORD lpdwBytesSent,LPOVERLAPPED lpOverlapped); + +#define WSAID_CONNECTEX {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} + + typedef WINBOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET s,LPOVERLAPPED lpOverlapped,DWORD dwFlags,DWORD dwReserved); + +#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} + +#define DE_REUSE_SOCKET TF_REUSE_SOCKET + +#define NLA_NAMESPACE_GUID {0x6642243a,0x3ba8,0x4aa6,{0xba,0xa5,0x2e,0xb,0xd7,0x1f,0xdd,0x83}} + +#define NLA_SERVICE_CLASS_GUID {0x37e515,0xb5c9,0x4a43,{0xba,0xda,0x8b,0x48,0xa8,0x7a,0xd2,0x39}} + +#define NLA_ALLUSERS_NETWORK 0x00000001 +#define NLA_FRIENDLY_NAME 0x00000002 + + typedef enum _NLA_BLOB_DATA_TYPE { + NLA_RAW_DATA = 0,NLA_INTERFACE = 1,NLA_802_1X_LOCATION = 2,NLA_CONNECTIVITY = 3,NLA_ICS = 4 + } NLA_BLOB_DATA_TYPE,*PNLA_BLOB_DATA_TYPE; + + typedef enum _NLA_CONNECTIVITY_TYPE { + NLA_NETWORK_AD_HOC = 0,NLA_NETWORK_MANAGED = 1,NLA_NETWORK_UNMANAGED = 2,NLA_NETWORK_UNKNOWN = 3 + } NLA_CONNECTIVITY_TYPE,*PNLA_CONNECTIVITY_TYPE; + + typedef enum _NLA_INTERNET { + NLA_INTERNET_UNKNOWN = 0,NLA_INTERNET_NO = 1,NLA_INTERNET_YES = 2 + } NLA_INTERNET,*PNLA_INTERNET; + + typedef struct _NLA_BLOB { + struct { + NLA_BLOB_DATA_TYPE type; + DWORD dwSize; + DWORD nextOffset; + } header; + union { + CHAR rawData[1]; + struct { + DWORD dwType; + DWORD dwSpeed; + CHAR adapterName[1]; + } interfaceData; + struct { + CHAR information[1]; + } locationData; + struct { + NLA_CONNECTIVITY_TYPE type; + NLA_INTERNET internet; + } connectivity; + struct { + struct { + DWORD speed; + DWORD type; + DWORD state; + WCHAR machineName[256]; + WCHAR sharedAdapterName[256]; + } remote; + } ICS; + } data; + } NLA_BLOB,*PNLA_BLOB,*LPNLA_BLOB; + +#ifdef BADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG + typedef struct _WSAMSG { + LPSOCKADDR name; + INT namelen; + LPWSABUF lpBuffers; + DWORD dwBufferCount; + WSABUF Control; + DWORD dwFlags; + } WSAMSG,*PWSAMSG,*LPWSAMSG; +#endif + + typedef struct _WSACMSGHDR { + SIZE_T cmsg_len; + INT cmsg_level; + INT cmsg_type; + } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; + +#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) +#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) +#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) +#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) +#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) +#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) +#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) + +#define MSG_TRUNC 0x0100 +#define MSG_CTRUNC 0x0200 +#define MSG_BCAST 0x0400 +#define MSG_MCAST 0x0800 + + typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg, + LPDWORD lpdwNumberOfBytesRecvd, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} + +#if(_WIN32_WINNT >= 0x0600) + typedef struct { + LPWSAMSG lpMsg; + DWORD dwFlags; + LPDWORD lpNumberOfBytesSent; + LPWSAOVERLAPPED lpOverlapped; + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine; + } WSASENDMSG, *LPWSASENDMSG; + + typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags, + LPDWORD lpNumberOfBytesSent, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} + +#endif /* (_WIN32_WINNT >= 0x0600) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _MSWSOCK_ */ diff --git a/app/src/main/jni/badvpn/misc/nonblocking.h b/app/src/main/jni/badvpn/misc/nonblocking.h new file mode 100644 index 0000000..fe82584 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/nonblocking.h @@ -0,0 +1,51 @@ +/** + * @file nonblocking.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for enabling non-blocking mode for a file descriptor. + */ + +#ifndef BADVPN_MISC_NONBLOCKING_H +#define BADVPN_MISC_NONBLOCKING_H + +#include +#include + +static int badvpn_set_nonblocking (int fd); + +int badvpn_set_nonblocking (int fd) +{ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + return 0; + } + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/nsskey.h b/app/src/main/jni/badvpn/misc/nsskey.h new file mode 100644 index 0000000..8268235 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/nsskey.h @@ -0,0 +1,118 @@ +/** + * @file nsskey.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for opening a NSS certificate and its private key. + */ + +#ifndef BADVPN_MISC_NSSKEY_H +#define BADVPN_MISC_NSSKEY_H + +#include + +#include +#include +#include +#include + +#include + +#include + +/** + * Opens a NSS certificate and its private key. + * + * @param name name of the certificate + * @param out_cert on success, the certificate will be returned here. Should be + * released with CERT_DestroyCertificate. + * @param out_key on success, the private key will be returned here. Should be + * released with SECKEY_DestroyPrivateKey. + * @return 1 on success, 0 on failure + */ +static int open_nss_cert_and_key (char *name, CERTCertificate **out_cert, SECKEYPrivateKey **out_key) WARN_UNUSED; + +static SECKEYPrivateKey * find_nss_private_key (char *name) +{ + SECKEYPrivateKey *key = NULL; + + PK11SlotList *slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL); + if (!slot_list) { + return NULL; + } + + PK11SlotListElement *slot_entry; + for (slot_entry = slot_list->head; !key && slot_entry; slot_entry = slot_entry->next) { + SECKEYPrivateKeyList *key_list = PK11_ListPrivKeysInSlot(slot_entry->slot, name, NULL); + if (!key_list) { + BLog(BLOG_ERROR, "PK11_ListPrivKeysInSlot failed"); + continue; + } + + SECKEYPrivateKeyListNode *key_node; + for (key_node = PRIVKEY_LIST_HEAD(key_list); !key && !PRIVKEY_LIST_END(key_node, key_list); key_node = PRIVKEY_LIST_NEXT(key_node)) { + char *key_name = PK11_GetPrivateKeyNickname(key_node->key); + if (!key_name || strcmp(key_name, name)) { + PORT_Free((void *)key_name); + continue; + } + PORT_Free((void *)key_name); + + key = SECKEY_CopyPrivateKey(key_node->key); + } + + SECKEY_DestroyPrivateKeyList(key_list); + } + + PK11_FreeSlotList(slot_list); + + return key; +} + +int open_nss_cert_and_key (char *name, CERTCertificate **out_cert, SECKEYPrivateKey **out_key) +{ + CERTCertificate *cert; + cert = CERT_FindCertByNicknameOrEmailAddr(CERT_GetDefaultCertDB(), name); + if (!cert) { + BLog(BLOG_ERROR, "CERT_FindCertByName failed (%d)", (int)PR_GetError()); + return 0; + } + + SECKEYPrivateKey *key = find_nss_private_key(name); + if (!key) { + BLog(BLOG_ERROR, "Failed to find private key"); + CERT_DestroyCertificate(cert); + return 0; + } + + *out_cert = cert; + *out_key = key; + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/offset.h b/app/src/main/jni/badvpn/misc/offset.h new file mode 100644 index 0000000..23b7683 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/offset.h @@ -0,0 +1,51 @@ +/** + * @file offset.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Macros for determining offsets of members in structs. + */ + +#ifndef BADVPN_MISC_OFFSET_H +#define BADVPN_MISC_OFFSET_H + +#include +#include + +/** + * Returns a pointer to a struct, given a pointer to its member. + */ +#define UPPER_OBJECT(_ptr, _object_type, _field_name) ((_object_type *)((char *)(_ptr) - offsetof(_object_type, _field_name))) + +/** + * Returns the offset of one struct member from another. + * Expands to an int. + */ +#define OFFSET_DIFF(_object_type, _field1, _field2) ((int)offsetof(_object_type, _field1) - (int)offsetof(_object_type, _field2)) + +#endif diff --git a/app/src/main/jni/badvpn/misc/open_standard_streams.h b/app/src/main/jni/badvpn/misc/open_standard_streams.h new file mode 100644 index 0000000..7fd6d41 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/open_standard_streams.h @@ -0,0 +1,54 @@ +/** + * @file open_standard_streams.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_OPEN_STANDARD_STREAMS_H +#define BADVPN_OPEN_STANDARD_STREAMS_H + +#ifndef BADVPN_USE_WINAPI +#include +#include +#include +#include +#endif + +static void open_standard_streams (void) +{ +#ifndef BADVPN_USE_WINAPI + int fd; + + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) { + close(fd); + } + } while (fd >= 0 && fd <= 2); +#endif +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/overflow.h b/app/src/main/jni/badvpn/misc/overflow.h new file mode 100644 index 0000000..9fde52a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/overflow.h @@ -0,0 +1,66 @@ +/** + * @file overflow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Functions for checking for overflow of integer addition. + */ + +#ifndef BADVPN_MISC_OVERFLOW_H +#define BADVPN_MISC_OVERFLOW_H + +#include +#include + +#define DEFINE_UNSIGNED_OVERFLOW(_name, _type, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + return (b > _max - a); \ +} + +#define DEFINE_SIGNED_OVERFLOW(_name, _type, _min, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + if ((a < 0) ^ (b < 0)) return 0; \ + if (a < 0) return -(a < _min - b); \ + return (a > _max - b); \ +} + +DEFINE_UNSIGNED_OVERFLOW(uint, unsigned int, UINT_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint8, uint8_t, UINT8_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint16, uint16_t, UINT16_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint32, uint32_t, UINT32_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint64, uint64_t, UINT64_MAX) + +DEFINE_SIGNED_OVERFLOW(int, int, INT_MIN, INT_MAX) +DEFINE_SIGNED_OVERFLOW(int8, int8_t, INT8_MIN, INT8_MAX) +DEFINE_SIGNED_OVERFLOW(int16, int16_t, INT16_MIN, INT16_MAX) +DEFINE_SIGNED_OVERFLOW(int32, int32_t, INT32_MIN, INT32_MAX) +DEFINE_SIGNED_OVERFLOW(int64, int64_t, INT64_MIN, INT64_MAX) + +#endif diff --git a/app/src/main/jni/badvpn/misc/packed.h b/app/src/main/jni/badvpn/misc/packed.h new file mode 100644 index 0000000..2175536 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/packed.h @@ -0,0 +1,51 @@ +/** + * @file packed.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Structure packing macros. + */ + +#ifndef BADVPN_PACKED_H +#define BADVPN_PACKED_H + +#ifdef _MSC_VER + +#define B_START_PACKED __pragma(pack(push, 1)) +#define B_END_PACKED __pragma(pack(pop)) +#define B_PACKED + +#else + +#define B_START_PACKED +#define B_END_PACKED +#define B_PACKED __attribute__((packed)) + +#endif + +#endif diff --git a/app/src/main/jni/badvpn/misc/parse_number.h b/app/src/main/jni/badvpn/misc/parse_number.h new file mode 100644 index 0000000..2601ebb --- /dev/null +++ b/app/src/main/jni/badvpn/misc/parse_number.h @@ -0,0 +1,304 @@ +/** + * @file parse_number.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Numeric string parsing. + */ + +#ifndef BADVPN_MISC_PARSE_NUMBER_H +#define BADVPN_MISC_PARSE_NUMBER_H + +#include +#include +#include +#include + +#include +#include + +// public parsing functions +static int decode_decimal_digit (char c); +static int decode_hex_digit (char c); +static int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; + +// public generation functions +static int compute_decimal_repr_size (uintmax_t x); +static void generate_decimal_repr (uintmax_t x, char *out, int repr_size); +static int generate_decimal_repr_string (uintmax_t x, char *out); + +// implementation follows + +// decimal representation of UINTMAX_MAX +static const char parse_number__uintmax_max_str[] = "18446744073709551615"; + +// make sure UINTMAX_MAX is what we think it is +static const char parse_number__uintmax_max_str_assert[(UINTMAX_MAX == UINTMAX_C(18446744073709551615)) ? 1 : -1]; + +static int decode_decimal_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } + + return -1; +} + +static int decode_hex_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': case 'a': return 10; + case 'B': case 'b': return 11; + case 'C': case 'c': return 12; + case 'D': case 'd': return 13; + case 'E': case 'e': return 14; + case 'F': case 'f': return 15; + } + + return -1; +} + +static int parse__no_overflow (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + while (str_len > 0) { + if (*str < '0' || *str > '9') { + return 0; + } + + n = 10 * n + (*str - '0'); + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + // we do not allow empty strings + if (str_len == 0) { + return 0; + } + + // remove leading zeros + while (str_len > 0 && *str == '0') { + str++; + str_len--; + } + + // detect overflow + if (str_len > sizeof(parse_number__uintmax_max_str) - 1 || + (str_len == sizeof(parse_number__uintmax_max_str) - 1 && memcmp(str, parse_number__uintmax_max_str, sizeof(parse_number__uintmax_max_str) - 1) > 0)) { + return 0; + } + + // will not overflow (but can still have invalid characters) + return parse__no_overflow(str, str_len, out); +} + +int parse_unsigned_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_integer_bin(str, strlen(str), out); +} + +int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == 0) { + return 0; + } + + uintmax_t n = 0; + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + for (size_t i = 0; i < chunk_length; i++) { + int digit = decode_decimal_digit(chunk_data[i]); + if (digit < 0) { + return 0; + } + if (n > UINTMAX_MAX / 10) { + return 0; + } + n *= 10; + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + } + }) + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + if (str_len == 0) { + return 0; + } + + while (str_len > 0) { + int digit = decode_hex_digit(*str); + if (digit < 0) { + return 0; + } + + if (n > UINTMAX_MAX / 16) { + return 0; + } + n *= 16; + + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_hex_integer_bin(str, strlen(str), out); +} + +int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) +{ + int sign = 1; + if (str_len > 0 && (str[0] == '+' || str[0] == '-')) { + sign = 1 - 2 * (str[0] == '-'); + str++; + str_len--; + } + + if (!parse_unsigned_integer_bin(str, str_len, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) +{ + return parse_signmag_integer_bin(str, strlen(str), out_sign, out_mag); +} + +int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) +{ + b_cstring_assert_range(cstr, offset, length); + + int sign = 1; + if (length > 0 && (b_cstring_at(cstr, offset) == '+' || b_cstring_at(cstr, offset) == '-')) { + sign = 1 - 2 * (b_cstring_at(cstr, offset) == '-'); + offset++; + length--; + } + + if (!parse_unsigned_integer_cstr(cstr, offset, length, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int compute_decimal_repr_size (uintmax_t x) +{ + int size = 0; + + do { + size++; + x /= 10; + } while (x > 0); + + return size; +} + +void generate_decimal_repr (uintmax_t x, char *out, int repr_size) +{ + ASSERT(out) + ASSERT(repr_size == compute_decimal_repr_size(x)) + + out += repr_size; + + do { + *(--out) = '0' + (x % 10); + x /= 10; + } while (x > 0); +} + +int generate_decimal_repr_string (uintmax_t x, char *out) +{ + ASSERT(out) + + int repr_size = compute_decimal_repr_size(x); + generate_decimal_repr(x, out, repr_size); + out[repr_size] = '\0'; + + return repr_size; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/print_macros.h b/app/src/main/jni/badvpn/misc/print_macros.h new file mode 100644 index 0000000..a07fdbe --- /dev/null +++ b/app/src/main/jni/badvpn/misc/print_macros.h @@ -0,0 +1,98 @@ +/** + * @file print_macros.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Format macros for printf() for non-standard compilers. + */ + +#ifndef BADVPN_PRINT_MACROS +#define BADVPN_PRINT_MACROS + +#ifdef _MSC_VER + +// size_t +#define PRIsz "Iu" + +// signed exact width (intN_t) +#define PRId8 "d" +#define PRIi8 "i" +#define PRId16 "d" +#define PRIi16 "i" +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRId64 "I64d" +#define PRIi64 "I64i" + +// unsigned exact width (uintN_t) +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIo16 "o" +#define PRIu16 "u" +#define PRIx16 "x" +#define PRIX16 "X" +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" + +// signed maximum width (intmax_t) +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +// unsigned maximum width (uintmax_t) +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +// signed pointer (intptr_t) +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// unsigned pointer (uintptr_t) +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +#else + +#include + +#define PRIsz "zu" + +#endif + +#endif diff --git a/app/src/main/jni/badvpn/misc/read_file.h b/app/src/main/jni/badvpn/misc/read_file.h new file mode 100644 index 0000000..e1862e5 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/read_file.h @@ -0,0 +1,98 @@ +/** + * @file read_file.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for reading a file into memory using stdio. + */ + +#ifndef BADVPN_MISC_READ_FILE_H +#define BADVPN_MISC_READ_FILE_H + +#include +#include +#include +#include + +static int read_file (const char *file, uint8_t **out_data, size_t *out_len) +{ + FILE *f = fopen(file, "r"); + if (!f) { + goto fail0; + } + + size_t buf_len = 0; + size_t buf_size = 128; + + uint8_t *buf = (uint8_t *)malloc(buf_size); + if (!buf) { + goto fail1; + } + + while (1) { + if (buf_len == buf_size) { + if (2 > SIZE_MAX / buf_size) { + goto fail; + } + size_t newsize = 2 * buf_size; + + uint8_t *newbuf = (uint8_t *)realloc(buf, newsize); + if (!newbuf) { + goto fail; + } + + buf = newbuf; + buf_size = newsize; + } + + size_t bytes = fread(buf + buf_len, 1, buf_size - buf_len, f); + if (bytes == 0) { + if (feof(f)) { + break; + } + goto fail; + } + + buf_len += bytes; + } + + fclose(f); + + *out_data = buf; + *out_len = buf_len; + return 1; + +fail: + free(buf); +fail1: + fclose(f); +fail0: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/read_write_int.h b/app/src/main/jni/badvpn/misc/read_write_int.h new file mode 100644 index 0000000..bc4ed2c --- /dev/null +++ b/app/src/main/jni/badvpn/misc/read_write_int.h @@ -0,0 +1,181 @@ +/** + * @file read_write_int.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_READ_WRITE_INT_H +#define BADVPN_READ_WRITE_INT_H + +#include + +static uint8_t badvpn_read_le8 (const char *c_ptr); +static uint16_t badvpn_read_le16 (const char *c_ptr); +static uint32_t badvpn_read_le32 (const char *c_ptr); +static uint64_t badvpn_read_le64 (const char *c_ptr); + +static uint8_t badvpn_read_be8 (const char *c_ptr); +static uint16_t badvpn_read_be16 (const char *c_ptr); +static uint32_t badvpn_read_be32 (const char *c_ptr); +static uint64_t badvpn_read_be64 (const char *c_ptr); + +static void badvpn_write_le8 (uint8_t x, char *c_ptr); +static void badvpn_write_le16 (uint16_t x, char *c_ptr); +static void badvpn_write_le32 (uint32_t x, char *c_ptr); +static void badvpn_write_le64 (uint64_t x, char *c_ptr); + +static void badvpn_write_be8 (uint8_t x, char *c_ptr); +static void badvpn_write_be16 (uint16_t x, char *c_ptr); +static void badvpn_write_be32 (uint32_t x, char *c_ptr); +static void badvpn_write_be64 (uint64_t x, char *c_ptr); + +static uint8_t badvpn_read_le8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_le16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[1] << 8) | ((uint16_t)ptr[0] << 0); +} + +static uint32_t badvpn_read_le32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[3] << 24) | ((uint32_t)ptr[2] << 16) | + ((uint32_t)ptr[1] << 8) | ((uint32_t)ptr[0] << 0); +} + +static uint64_t badvpn_read_le64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[7] << 56) | ((uint64_t)ptr[6] << 48) | + ((uint64_t)ptr[5] << 40) | ((uint64_t)ptr[4] << 32) | + ((uint64_t)ptr[3] << 24) | ((uint64_t)ptr[2] << 16) | + ((uint64_t)ptr[1] << 8) | ((uint64_t)ptr[0] << 0); +} + +static uint8_t badvpn_read_be8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_be16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[0] << 8) | ((uint16_t)ptr[1] << 0); +} + +static uint32_t badvpn_read_be32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | + ((uint32_t)ptr[2] << 8) | ((uint32_t)ptr[3] << 0); +} + +static uint64_t badvpn_read_be64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[0] << 56) | ((uint64_t)ptr[1] << 48) | + ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[3] << 32) | + ((uint64_t)ptr[4] << 24) | ((uint64_t)ptr[5] << 16) | + ((uint64_t)ptr[6] << 8) | ((uint64_t)ptr[7] << 0); +} + +static void badvpn_write_le8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_le16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[7] = x >> 56; + ptr[6] = x >> 48; + ptr[5] = x >> 40; + ptr[4] = x >> 32; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_be8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_be16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 8; + ptr[1] = x >> 0; +} + +static void badvpn_write_be32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 24; + ptr[1] = x >> 16; + ptr[2] = x >> 8; + ptr[3] = x >> 0; +} + +static void badvpn_write_be64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 56; + ptr[1] = x >> 48; + ptr[2] = x >> 40; + ptr[3] = x >> 32; + ptr[4] = x >> 24; + ptr[5] = x >> 16; + ptr[6] = x >> 8; + ptr[7] = x >> 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/socks_proto.h b/app/src/main/jni/badvpn/misc/socks_proto.h new file mode 100644 index 0000000..41f5a1f --- /dev/null +++ b/app/src/main/jni/badvpn/misc/socks_proto.h @@ -0,0 +1,118 @@ +/** + * @file socks_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the SOCKS protocol. + */ + +#ifndef BADVPN_MISC_SOCKS_PROTO_H +#define BADVPN_MISC_SOCKS_PROTO_H + +#include + +#include + +#define SOCKS_VERSION 0x05 + +#define SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED 0x00 +#define SOCKS_METHOD_GSSAPI 0x01 +#define SOCKS_METHOD_USERNAME_PASSWORD 0x02 +#define SOCKS_METHOD_NO_ACCEPTABLE_METHODS 0xFF + +#define SOCKS_CMD_CONNECT 0x01 +#define SOCKS_CMD_BIND 0x02 +#define SOCKS_CMD_UDP_ASSOCIATE 0x03 + +#define SOCKS_ATYP_IPV4 0x01 +#define SOCKS_ATYP_DOMAINNAME 0x03 +#define SOCKS_ATYP_IPV6 0x04 + +#define SOCKS_REP_SUCCEEDED 0x00 +#define SOCKS_REP_GENERAL_FAILURE 0x01 +#define SOCKS_REP_CONNECTION_NOT_ALLOWED 0x02 +#define SOCKS_REP_NETWORK_UNREACHABLE 0x03 +#define SOCKS_REP_HOST_UNREACHABLE 0x04 +#define SOCKS_REP_CONNECTION_REFUSED 0x05 +#define SOCKS_REP_TTL_EXPIRED 0x06 +#define SOCKS_REP_COMMAND_NOT_SUPPORTED 0x07 +#define SOCKS_REP_ADDRESS_TYPE_NOT_SUPPORTED 0x08 + +B_START_PACKED +struct socks_client_hello_header { + uint8_t ver; + uint8_t nmethods; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_client_hello_method { + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_server_hello { + uint8_t ver; + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_request_header { + uint8_t ver; + uint8_t cmd; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_reply_header { + uint8_t ver; + uint8_t rep; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv4 { + uint32_t addr; + uint16_t port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv6 { + uint8_t addr[16]; + uint16_t port; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/misc/sslsocket.h b/app/src/main/jni/badvpn/misc/sslsocket.h new file mode 100644 index 0000000..71e43c2 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/sslsocket.h @@ -0,0 +1,48 @@ +/** + * @file sslsocket.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Structure for moving around sockets, possibly together with SSL compoments. + */ + +#ifndef BADVPN_MISC_SSLSOCKET_H +#define BADVPN_MISC_SSLSOCKET_H + +#include + +#include +#include + +typedef struct { + BConnection con; + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; +} sslsocket; + +#endif diff --git a/app/src/main/jni/badvpn/misc/stdbuf_cmdline.h b/app/src/main/jni/badvpn/misc/stdbuf_cmdline.h new file mode 100644 index 0000000..685c9ae --- /dev/null +++ b/app/src/main/jni/badvpn/misc/stdbuf_cmdline.h @@ -0,0 +1,93 @@ +/** + * @file stdbuf_cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Builds command line for running a program via stdbuf. + */ + +#ifndef BADVPN_STDBUF_CMDLINE_H +#define BADVPN_STDBUF_CMDLINE_H + +#include + +#include +#include +#include + +#define STDBUF_EXEC "/usr/bin/stdbuf" + +/** + * Builds the initial part of command line for calling a program via stdbuf + * with standard output buffering set to line-buffered. + * + * @param out {@link CmdLine} to append the result to. Note than on failure, only + * some part of the cmdline may have been appended. + * @param exec path to the executable. Must not contain nulls. + * @param exec_len number of characters in exec + * @return 1 on success, 0 on failure + */ +static int build_stdbuf_cmdline (CmdLine *out, const char *exec, size_t exec_len) WARN_UNUSED; + +int build_stdbuf_cmdline (CmdLine *out, const char *exec, size_t exec_len) +{ + ASSERT(!memchr(exec, '\0', exec_len)) + + if (!CmdLine_AppendMulti(out, 3, STDBUF_EXEC, "-o", "L")) { + goto fail1; + } + + if (exec[0] == '/') { + if (!CmdLine_AppendNoNull(out, exec, exec_len)) { + goto fail1; + } + } else { + bsize_t size = bsize_add(bsize_fromsize(exec_len), bsize_fromsize(3)); + char *real_exec = BAllocSize(size); + if (!real_exec) { + goto fail1; + } + + memcpy(real_exec, "./", 2); + memcpy(real_exec + 2, exec, exec_len); + real_exec[2 + exec_len] = '\0'; + + int res = CmdLine_Append(out, real_exec); + free(real_exec); + if (!res) { + goto fail1; + } + } + + return 1; + +fail1: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/strdup.h b/app/src/main/jni/badvpn/misc/strdup.h new file mode 100644 index 0000000..2e475bb --- /dev/null +++ b/app/src/main/jni/badvpn/misc/strdup.h @@ -0,0 +1,86 @@ +/** + * @file strdup.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Allocate memory for a string and copy it there. + */ + +#ifndef BADVPN_STRDUP_H +#define BADVPN_STRDUP_H + +#include +#include +#include + +#include + +/** + * Allocate and copy a null-terminated string. + */ +static char * b_strdup (const char *str) +{ + ASSERT(str) + + size_t len = strlen(str); + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len + 1); + + return s; +} + +/** + * Allocate memory for a null-terminated string and use the + * given data as its contents. A null terminator is appended + * after the specified data. + */ +static char * b_strdup_bin (const char *str, size_t len) +{ + ASSERT(str) + + if (len == SIZE_MAX) { + return NULL; + } + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len); + s[len] = '\0'; + + return s; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/string_begins_with.h b/app/src/main/jni/badvpn/misc/string_begins_with.h new file mode 100644 index 0000000..3c9c5e9 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/string_begins_with.h @@ -0,0 +1,96 @@ +/** + * @file string_begins_with.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for checking if a string begins with a given string. + */ + +#ifndef BADVPN_MISC_STRING_BEGINS_WITH +#define BADVPN_MISC_STRING_BEGINS_WITH + +#include +#include + +#include + +static size_t data_begins_with (const char *str, size_t str_len, const char *needle) +{ + ASSERT(strlen(needle) > 0) + + size_t len = 0; + + while (str_len > 0 && *needle) { + if (*str != *needle) { + return 0; + } + str++; + str_len--; + needle++; + len++; + } + + if (*needle) { + return 0; + } + + return len; +} + +static size_t string_begins_with (const char *str, const char *needle) +{ + ASSERT(strlen(needle) > 0) + + return data_begins_with(str, strlen(str), needle); +} + +static size_t data_begins_with_bin (const char *str, size_t str_len, const char *needle, size_t needle_len) +{ + ASSERT(needle_len > 0) + + size_t len = 0; + + while (str_len > 0 && needle_len > 0) { + if (*str != *needle) { + return 0; + } + str++; + str_len--; + needle++; + needle_len--; + len++; + } + + if (needle_len > 0) { + return 0; + } + + return len; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/substring.h b/app/src/main/jni/badvpn/misc/substring.h new file mode 100644 index 0000000..b3a8fff --- /dev/null +++ b/app/src/main/jni/badvpn/misc/substring.h @@ -0,0 +1,81 @@ +#include + +#include + +static void build_substring_backtrack_table (const char *str, size_t len, size_t *out_table) +{ + ASSERT(len > 0) + + size_t x = 0; + + for (size_t i = 1; i < len; i++) { + out_table[i] = x; + while (x > 0 && str[i] != str[x]) { + x = out_table[x]; + } + if (str[i] == str[x]) { + x++; + } + } +} + +static int find_substring (const char *text, size_t text_len, const char *word, size_t word_len, const size_t *table, size_t *out_position) +{ + ASSERT(word_len > 0) + + size_t x = 0; + + for (size_t i = 0; i < text_len; i++) { + while (x > 0 && text[i] != word[x]) { + x = table[x]; + } + if (text[i] == word[x]) { + if (x + 1 == word_len) { + *out_position = i - x; + return 1; + } + x++; + } + } + + return 0; +} + +static void build_substring_backtrack_table_reverse (const char *str, size_t len, size_t *out_table) +{ + ASSERT(len > 0) + + size_t x = 0; + + for (size_t i = 1; i < len; i++) { + out_table[i] = x; + while (x > 0 && str[len - 1 - i] != str[len - 1 - x]) { + x = out_table[x]; + } + if (str[len - 1 - i] == str[len - 1 - x]) { + x++; + } + } +} + +static int find_substring_reverse (const char *text, size_t text_len, const char *word, size_t word_len, const size_t *table, size_t *out_position) +{ + ASSERT(word_len > 0) + + size_t x = 0; + + for (size_t i = 0; i < text_len; i++) { + while (x > 0 && text[text_len - 1 - i] != word[word_len - 1 - x]) { + x = table[x]; + } + if (text[text_len - 1 - i] == word[word_len - 1 - x]) { + if (x + 1 == word_len) { + *out_position = (text_len - 1 - i); + return 1; + } + x++; + } + } + + return 0; +} diff --git a/app/src/main/jni/badvpn/misc/udp_proto.h b/app/src/main/jni/badvpn/misc/udp_proto.h new file mode 100644 index 0000000..23ec69a --- /dev/null +++ b/app/src/main/jni/badvpn/misc/udp_proto.h @@ -0,0 +1,170 @@ +/** + * @file udp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the UDP protocol. + */ + +#ifndef BADVPN_MISC_UDP_PROTO_H +#define BADVPN_MISC_UDP_PROTO_H + +#include + +#include +#include +#include +#include +#include + +B_START_PACKED +struct udp_header { + uint16_t source_port; + uint16_t dest_port; + uint16_t length; + uint16_t checksum; +} B_PACKED; +B_END_PACKED + +static uint32_t udp_checksum_summer (const char *data, uint16_t len) +{ + ASSERT(len % 2 == 0) + + uint32_t t = 0; + + for (uint16_t i = 0; i < len / 2; i++) { + t += badvpn_read_be16(data + 2 * i); + } + + return t; +} + +static uint16_t udp_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, uint32_t source_addr, uint32_t dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((char *)&source_addr, sizeof(source_addr)); + t += udp_checksum_summer((char *)&dest_addr, sizeof(dest_addr)); + + uint16_t x; + x = hton16(IPV4_PROTOCOL_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton16(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + x = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&x, sizeof(x)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static uint16_t udp_ip6_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, const uint8_t *source_addr, const uint8_t *dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((const char *)source_addr, 16); + t += udp_checksum_summer((const char *)dest_addr, 16); + + uint32_t x; + x = hton32(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton32(IPV6_NEXT_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + uint16_t y; + y = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&y, sizeof(y)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static int udp_check (const uint8_t *data, int data_len, struct udp_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // parse UDP header + if (data_len < sizeof(struct udp_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + data += sizeof(*out_header); + data_len -= sizeof(*out_header); + + // verify UDP payload + int udp_length = ntoh16(out_header->length); + if (udp_length < sizeof(*out_header)) { + return 0; + } + if (udp_length > sizeof(*out_header) + data_len) { + return 0; + } + + // ignore stray data + data_len = udp_length - sizeof(*out_header); + + *out_payload = (uint8_t *)data; + *out_payload_len = data_len; + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/unicode_funcs.h b/app/src/main/jni/badvpn/misc/unicode_funcs.h new file mode 100644 index 0000000..2442e7f --- /dev/null +++ b/app/src/main/jni/badvpn/misc/unicode_funcs.h @@ -0,0 +1,232 @@ +/** + * @file unicode_funcs.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UNICODE_FUNCS_H +#define BADVPN_UNICODE_FUNCS_H + +#include +#include +#include +#include +#include +#include + +/** + * Decodes UTF-16 data as bytes into an allocated null-terminated UTF-8 string. + * + * @param data UTF-16 data, in big endian + * @param data_len size of data in bytes + * @param out_is_error if not NULL and the function returns a string, + * *out_is_error will be set to 0 or 1, indicating + * whether there have been errors decoding the input. + * A null decoded character is treated as an error. + * @return An UTF-8 null-terminated string which can be freed with free(), + * or NULL if out of memory. + */ +static char * unicode_decode_utf16_to_utf8 (const uint8_t *data, size_t data_len, int *out_is_error); + +/** + * Decodes UTF-8 data into UTF-16 data as bytes. + * + * @param data UTF-8 data + * @param data_len size of data in bytes + * @param out output buffer + * @param out_avail number of bytes available in output buffer + * @param out_len if not NULL, *out_len will contain the number of bytes + * required to store the resulting data (or overflow) + * @param out_is_error if not NULL, *out_is_error will contain 0 or 1, + * indicating whether there have been errors decoding + * the input + */ +static void unicode_decode_utf8_to_utf16 (const uint8_t *data, size_t data_len, uint8_t *out, size_t out_avail, bsize_t *out_len, int *out_is_error); + +static char * unicode_decode_utf16_to_utf8 (const uint8_t *data, size_t data_len, int *out_is_error) +{ + // will build the resulting UTF-8 string by appending to ExpString + ExpString str; + if (!ExpString_Init(&str)) { + goto fail0; + } + + // init UTF-16 decoder + Utf16Decoder decoder; + Utf16Decoder_Init(&decoder); + + // set initial input and input matching positions + size_t i_in = 0; + size_t i_ch = 0; + + int error = 0; + + while (i_in < data_len) { + // read two input bytes from the input position + uint8_t x = data[i_in++]; + if (i_in == data_len) { + break; + } + uint8_t y = data[i_in++]; + + // combine them into a 16-bit value + uint16_t xy = (((uint16_t)x << 8) | (uint16_t)y); + + // give the 16-bit value to the UTF-16 decoder and maybe + // receive a Unicode character back + uint32_t ch; + if (!Utf16Decoder_Input(&decoder, xy, &ch)) { + continue; + } + + if (!error) { + // encode the Unicode character back into UTF-16 + uint16_t chenc[2]; + int chenc_n = Utf16Encoder_EncodeCharacter(ch, chenc); + ASSERT(chenc_n > 0) + + // match the result with input + for (int chenc_i = 0; chenc_i < chenc_n; chenc_i++) { + uint8_t cx = (chenc[chenc_i] >> 8); + uint8_t cy = (chenc[chenc_i] & 0xFF); + + if (i_ch >= data_len || data[i_ch] != cx) { + error = 1; + break; + } + i_ch++; + + if (i_ch >= data_len || data[i_ch] != cy) { + error = 1; + break; + } + i_ch++; + } + } + + // we don't like null Unicode characters because we're building a + // null-terminated UTF-8 string + if (ch == 0) { + error = 1; + continue; + } + + // encode the Unicode character into UTF-8 + uint8_t enc[5]; + int enc_n = Utf8Encoder_EncodeCharacter(ch, enc); + ASSERT(enc_n > 0) + + // append the resulting UTF-8 bytes to the result string + enc[enc_n] = 0; + if (!ExpString_Append(&str, enc)) { + goto fail1; + } + } + + // check if we matched the whole input string when encoding back + if (i_ch < data_len) { + error = 1; + } + + if (out_is_error) { + *out_is_error = error; + } + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +static void unicode_decode_utf8_to_utf16 (const uint8_t *data, size_t data_len, uint8_t *out, size_t out_avail, bsize_t *out_len, int *out_is_error) +{ + Utf8Decoder decoder; + Utf8Decoder_Init(&decoder); + + size_t i_in = 0; + size_t i_ch = 0; + + bsize_t len = bsize_fromsize(0); + + int error = 0; + + while (i_in < data_len) { + uint8_t x = data[i_in++]; + + uint32_t ch; + if (!Utf8Decoder_Input(&decoder, x, &ch)) { + continue; + } + + if (!error) { + uint8_t chenc[4]; + int chenc_n = Utf8Encoder_EncodeCharacter(ch, chenc); + ASSERT(chenc_n > 0) + + for (int chenc_i = 0; chenc_i < chenc_n; chenc_i++) { + if (i_ch >= data_len || data[i_ch] != chenc[chenc_i]) { + error = 1; + break; + } + i_ch++; + } + } + + uint16_t enc[2]; + int enc_n = Utf16Encoder_EncodeCharacter(ch, enc); + ASSERT(enc_n > 0) + + len = bsize_add(len, bsize_fromsize(2 * enc_n)); + + for (int enc_i = 0; enc_i < enc_n; enc_i++) { + if (out_avail == 0) { + break; + } + *(out++) = (enc[enc_i] >> 8); + out_avail--; + + if (out_avail == 0) { + break; + } + *(out++) = (enc[enc_i] & 0xFF); + out_avail--; + } + } + + if (i_ch < data_len) { + error = 1; + } + + if (out_len) { + *out_len = len; + } + if (out_is_error) { + *out_is_error = error; + } +} + +#endif diff --git a/app/src/main/jni/badvpn/misc/version.h b/app/src/main/jni/badvpn/misc/version.h new file mode 100644 index 0000000..4468849 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/version.h @@ -0,0 +1,41 @@ +/** + * @file version.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Product information definitions. + */ + +#ifndef BADVPN_MISC_VERSION_H +#define BADVPN_MISC_VERSION_H + +#define GLOBAL_PRODUCT_NAME "BadVPN" +#define GLOBAL_VERSION "1.999.127" +#define GLOBAL_COPYRIGHT_NOTICE "Copyright (C) 2010 Ambroz Bizjak " + +#endif diff --git a/app/src/main/jni/badvpn/misc/write_file.h b/app/src/main/jni/badvpn/misc/write_file.h new file mode 100644 index 0000000..97b1c19 --- /dev/null +++ b/app/src/main/jni/badvpn/misc/write_file.h @@ -0,0 +1,104 @@ +/** + * @file write_file.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_WRITE_FILE_H +#define BADVPN_WRITE_FILE_H + +#include +#include +#include + +#include +#include + +static int write_file (const char *file, const uint8_t *data, size_t len) +{ + FILE *f = fopen(file, "w"); + if (!f) { + goto fail0; + } + + while (len > 0) { + size_t res = fwrite(data, 1, len, f); + if (res == 0) { + goto fail1; + } + + ASSERT(res <= len) + + data += res; + len -= res; + } + + if (fclose(f) != 0) { + return 0; + } + + return 1; + +fail1: + fclose(f); +fail0: + return 0; +} + +static int write_file_cstring (const char *file, b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + + FILE *f = fopen(file, "w"); + if (!f) { + goto fail0; + } + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + size_t res = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, f); + if (res == 0) { + goto fail1; + } + ASSERT(res <= chunk_length - chunk_pos) + chunk_pos += res; + } + }) + + if (fclose(f) != 0) { + return 0; + } + + return 1; + +fail1: + fclose(f); +fail0: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/ncd-request/CMakeLists.txt b/app/src/main/jni/badvpn/ncd-request/CMakeLists.txt new file mode 100644 index 0000000..61447fd --- /dev/null +++ b/app/src/main/jni/badvpn/ncd-request/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(badvpn-ncd-request + ncd-request.c +) +target_link_libraries(badvpn-ncd-request ncdrequest ncdvalgenerator ncdvalparser) + +install( + TARGETS badvpn-ncd-request + RUNTIME DESTINATION bin +) diff --git a/app/src/main/jni/badvpn/ncd-request/ncd-request.c b/app/src/main/jni/badvpn/ncd-request/ncd-request.c new file mode 100644 index 0000000..5b44bdb --- /dev/null +++ b/app/src/main/jni/badvpn/ncd-request/ncd-request.c @@ -0,0 +1,224 @@ +/** + * @file ncd-request.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void client_handler_error (void *user); +static void client_handler_connected (void *user); +static void request_handler_sent (void *user); +static void request_handler_reply (void *user, NCDValMem reply_mem, NCDValRef reply_value); +static void request_handler_finished (void *user, int is_error); +static int write_all (int fd, const uint8_t *data, size_t len); +static int make_connect_addr (const char *str, struct BConnection_addr *out_addr); + +NCDValMem request_mem; +NCDValRef request_value; +BReactor reactor; +NCDRequestClient client; +NCDRequestClientRequest request; +int have_request; + +int main (int argc, char *argv[]) +{ + int res = 1; + + if (argc != 3) { + fprintf(stderr, "Usage: %s < unix: / tcp:
: > \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + char *connect_address = argv[1]; + char *request_payload_string = argv[2]; + + BLog_InitStderr(); + + BTime_Init(); + + NCDValMem_Init(&request_mem); + + if (!NCDValParser_Parse(request_payload_string, strlen(request_payload_string), &request_mem, &request_value)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_Init failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + struct BConnection_addr addr; + if (!make_connect_addr(connect_address, &addr)) { + goto fail2; + } + + if (!NCDRequestClient_Init(&client, addr, &reactor, NULL, client_handler_error, client_handler_connected)) { + BLog(BLOG_ERROR, "NCDRequestClient_Init failed"); + goto fail2; + } + + have_request = 0; + + res = BReactor_Exec(&reactor); + + if (have_request) { + NCDRequestClientRequest_Free(&request); + } + NCDRequestClient_Free(&client); +fail2: + BReactor_Free(&reactor); +fail1: + NCDValMem_Free(&request_mem); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + return res; +} + +static int make_connect_addr (const char *str, struct BConnection_addr *out_addr) +{ + size_t i; + + if (i = string_begins_with(str, "unix:")) { + *out_addr = BConnection_addr_unix(str + i, strlen(str + i)); + } + else if (i = string_begins_with(str, "tcp:")) { + BAddr baddr; + if (!BAddr_Parse2(&baddr, (char *)str + i, NULL, 0, 1)) { + BLog(BLOG_ERROR, "failed to parse tcp address"); + return 0; + } + + *out_addr = BConnection_addr_baddr(baddr); + } + else { + BLog(BLOG_ERROR, "address must start with unix: or tcp:"); + return 0; + } + + return 1; +} + +static void client_handler_error (void *user) +{ + BLog(BLOG_ERROR, "client error"); + + BReactor_Quit(&reactor, 1); +} + +static void client_handler_connected (void *user) +{ + ASSERT(!have_request) + + if (!NCDRequestClientRequest_Init(&request, &client, request_value, NULL, request_handler_sent, request_handler_reply, request_handler_finished)) { + BLog(BLOG_ERROR, "NCDRequestClientRequest_Init failed"); + BReactor_Quit(&reactor, 1); + return; + } + + have_request = 1; +} + +static void request_handler_sent (void *user) +{ + ASSERT(have_request) +} + +static void request_handler_reply (void *user, NCDValMem reply_mem, NCDValRef reply_value) +{ + ASSERT(have_request) + + char *str = NCDValGenerator_Generate(reply_value); + if (!str) { + BLog(BLOG_ERROR, "NCDValGenerator_Generate failed"); + goto fail0; + } + + if (!write_all(1, (uint8_t *)str, strlen(str))) { + goto fail1; + } + if (!write_all(1, (const uint8_t *)"\n", 1)) { + goto fail1; + } + + free(str); + NCDValMem_Free(&reply_mem); + return; + +fail1: + free(str); +fail0: + NCDValMem_Free(&reply_mem); + BReactor_Quit(&reactor, 1); +} + +static void request_handler_finished (void *user, int is_error) +{ + if (is_error) { + BLog(BLOG_ERROR, "request error"); + BReactor_Quit(&reactor, 1); + return; + } + + BReactor_Quit(&reactor, 0); +} + +static int write_all (int fd, const uint8_t *data, size_t len) +{ + while (len > 0) { + ssize_t res = write(fd, data, len); + if (res <= 0) { + BLog(BLOG_ERROR, "write failed"); + return 0; + } + data += res; + len -= res; + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/CMakeLists.txt b/app/src/main/jni/badvpn/ncd/CMakeLists.txt new file mode 100644 index 0000000..8c384ae --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/CMakeLists.txt @@ -0,0 +1,211 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(NCD_ADDITIONAL_SOURCES) +set(NCD_ADDITIONAL_LIBS) + +if (NOT EMSCRIPTEN) + if (BADVPN_USE_LINUX_RFKILL) + list(APPEND NCD_ADDITIONAL_SOURCES + extra/NCDRfkillMonitor.c + modules/net_backend_rfkill.c + ) + endif () + + if (BADVPN_USE_LINUX_INPUT) + list(APPEND NCD_ADDITIONAL_SOURCES + modules/sys_evdev.c + ) + endif () + + if (BADVPN_USE_INOTIFY) + list(APPEND NCD_ADDITIONAL_SOURCES + modules/sys_watch_directory.c + ) + endif () + + badvpn_add_library(ncdinterfacemonitor "base;system" "" extra/NCDInterfaceMonitor.c) + + badvpn_add_library(ncdrequest "base;system;ncdvalgenerator;ncdvalparser" "" extra/NCDRequestClient.c) + + list(APPEND NCD_ADDITIONAL_SOURCES + extra/NCDIfConfig.c + extra/build_cmdline.c + extra/NCDBProcessOpts.c + modules/command_template.c + modules/event_template.c + modules/regex_match.c + modules/run.c + modules/runonce.c + modules/daemon.c + modules/net_backend_waitdevice.c + modules/net_backend_waitlink.c + modules/net_backend_badvpn.c + modules/net_backend_wpa_supplicant.c + modules/net_up.c + modules/net_dns.c + modules/net_iptables.c + modules/net_ipv4_addr.c + modules/net_ipv4_route.c + modules/net_ipv4_dhcp.c + modules/net_ipv4_arp_probe.c + modules/net_watch_interfaces.c + modules/sys_watch_input.c + modules/sys_watch_usb.c + modules/sys_request_server.c + modules/net_ipv6_wait_dynamic_addr.c + modules/sys_request_client.c + modules/reboot.c + modules/net_ipv6_addr.c + modules/net_ipv6_route.c + modules/socket.c + modules/sys_start_process.c + modules/load_module.c + ) + + list(APPEND NCD_ADDITIONAL_LIBS + dhcpclient arpprobe ncdinterfacemonitor ncdrequest udevmonitor badvpn_random dl + ) +endif () + +badvpn_add_library(ncdtokenizer "base" "" NCDConfigTokenizer.c) + +badvpn_add_library(ncdstringindex "base" "" NCDStringIndex.c) + +badvpn_add_library(ncdval "base;ncdstringindex" "" NCDVal.c) + +badvpn_add_library(ncdvalgenerator "base;ncdval" "" NCDValGenerator.c) + +badvpn_add_library(ncdvalparser "base;ncdval;ncdtokenizer;ncdvalcons" "" NCDValParser.c) + +badvpn_add_library(ncdast "" "" NCDAst.c) + +badvpn_add_library(ncdconfigparser "base;ncdtokenizer;ncdast" "" NCDConfigParser.c) + +badvpn_add_library(ncdsugar "ncdast" "" NCDSugar.c) + +badvpn_add_library(ncdvalcons "ncdval" "" NCDValCons.c) + +badvpn_add_library(ncdbuildprogram "base;ncdast;ncdconfigparser" "" NCDBuildProgram.c) + +badvpn_add_library(ncdobject "" "" NCDObject.c) + +badvpn_add_library(ncdmodule "base;ncdobject;ncdstringindex;ncdval" "" NCDModule.c) + +set(NCDINTERPRETER_SOURCES + NCDInterpreter.c + NCDModuleIndex.c + NCDInterpProcess.c + NCDInterpProg.c + NCDPlaceholderDb.c + NCDMethodIndex.c + extra/BEventLock.c + extra/NCDBuf.c + modules/var.c + modules/list.c + modules/depend.c + modules/multidepend.c + modules/dynamic_depend.c + modules/concat.c + modules/if.c + modules/strcmp.c + modules/logical.c + modules/sleep.c + modules/print.c + modules/blocker.c + modules/spawn.c + modules/imperative.c + modules/ref.c + modules/index.c + modules/alias.c + modules/process_manager.c + modules/ondemand.c + modules/foreach.c + modules/choose.c + modules/from_string.c + modules/to_string.c + modules/value.c + modules/try.c + modules/exit.c + modules/getargs.c + modules/arithmetic.c + modules/parse.c + modules/valuemetic.c + modules/file.c + modules/netmask.c + modules/implode.c + modules/call2.c + modules/assert.c + modules/explode.c + modules/net_ipv4_addr_in_network.c + modules/net_ipv6_addr_in_network.c + modules/timer.c + modules/file_open.c + modules/backtrack.c + modules/depend_scope.c + modules/substr.c + modules/log.c + modules/buffer.c + modules/getenv.c + ${NCD_ADDITIONAL_SOURCES} +) +set(NCDINTERPRETER_LIBS + base system flow flowextra ncdval ncdstringindex ncdvalgenerator ncdvalparser + ncdconfigparser ncdsugar ncdobject ncdmodule ${NCD_ADDITIONAL_LIBS}) +badvpn_add_library(ncdinterpreter "${NCDINTERPRETER_LIBS}" "" "${NCDINTERPRETER_SOURCES}") + +if (BADVPN_USE_LINUX_INPUT) + string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}") + execute_process(COMMAND ${CMAKE_C_COMPILER} ${FLAGS_LIST} -E ${CMAKE_CURRENT_SOURCE_DIR}/include_linux_input.c + RESULT_VARIABLE LINUX_INPUT_PREPROCESS_RESULT + OUTPUT_VARIABLE LINUX_INPUT_PREPROCESS_OUTPUT) + if (NOT LINUX_INPUT_PREPROCESS_RESULT EQUAL 0) + message(FATAL_ERROR "failed to preprocess linux/input.h include") + endif () + + string(REGEX MATCH "\"(/[^\"]+/linux/input.h)\"" LINUX_INPUT_MATCH ${LINUX_INPUT_PREPROCESS_OUTPUT}) + if (NOT LINUX_INPUT_MATCH) + message(FATAL_ERROR "failed to match preprocessor output for path of linux/input.h") + endif () + set(LINUX_INPUT_H_PATH ${CMAKE_MATCH_1}) + + message(STATUS "Generating linux_input_names.h from ${LINUX_INPUT_H_PATH}") + + execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/parse_linux_input.sh + ${LINUX_INPUT_H_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/linux_input_names.h + RESULT_VARIABLE LINUX_INPUT_PARSE_RESULT) + if (NOT LINUX_INPUT_PARSE_RESULT EQUAL 0) + message(FATAL_ERROR "failed to generate linux_input_names.h") + endif () +endif () + +if (NOT EMSCRIPTEN) + add_executable(badvpn-ncd ncd.c) + target_link_libraries(badvpn-ncd ncdinterpreter ncdbuildprogram) + + install( + TARGETS badvpn-ncd + RUNTIME DESTINATION bin + ) +endif () + +if (EMSCRIPTEN) + add_executable(emncd emncd.c) + target_link_libraries(emncd ncdinterpreter) + + add_custom_command( + OUTPUT emncd.bc + DEPENDS emncd + COMMAND cp emncd emncd.bc + ) + + add_custom_command( + OUTPUT emncd.js + DEPENDS emncd.bc + COMMAND + ${CMAKE_C_COMPILER} emncd.bc -o emncd.js -O2 + -s EXPORTED_FUNCTIONS=\"['_breactor_timer_cb','_main','_emncd_start','_emncd_stop']\" + ) + + add_custom_target(emncd_js ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/emncd.js) +endif () diff --git a/app/src/main/jni/badvpn/ncd/NCDAst.c b/app/src/main/jni/badvpn/ncd/NCDAst.c new file mode 100644 index 0000000..2b229e3 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDAst.c @@ -0,0 +1,1022 @@ +/** + * @file NCDAst.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include "NCDAst.h" + +struct NCDValue__list_element { + LinkedList1Node list_node; + NCDValue v; +}; + +struct NCDValue__map_element { + LinkedList1Node list_node; + NCDValue key; + NCDValue val; +}; + +struct ProgramElem { + LinkedList1Node elems_list_node; + NCDProgramElem elem; +}; + +struct BlockStatement { + LinkedList1Node statements_list_node; + NCDStatement s; +}; + +struct IfBlockIf { + LinkedList1Node ifs_list_node; + NCDIf ifc; +}; + +static void value_assert (NCDValue *o) +{ + switch (o->type) { + case NCDVALUE_STRING: + case NCDVALUE_LIST: + case NCDVALUE_MAP: + case NCDVALUE_VAR: + return; + default: + ASSERT(0); + } +} + +void NCDValue_Free (NCDValue *o) +{ + switch (o->type) { + case NCDVALUE_STRING: { + free(o->string); + } break; + + case NCDVALUE_LIST: { + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->list)) { + struct NCDValue__list_element *e = UPPER_OBJECT(n, struct NCDValue__list_element, list_node); + + NCDValue_Free(&e->v); + LinkedList1_Remove(&o->list, &e->list_node); + free(e); + } + } break; + + case NCDVALUE_MAP: { + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->map_list)) { + struct NCDValue__map_element *e = UPPER_OBJECT(n, struct NCDValue__map_element, list_node); + + LinkedList1_Remove(&o->map_list, &e->list_node); + NCDValue_Free(&e->key); + NCDValue_Free(&e->val); + free(e); + } + } break; + + case NCDVALUE_VAR: { + free(o->var_name); + } break; + + default: + ASSERT(0); + } +} + +int NCDValue_Type (NCDValue *o) +{ + value_assert(o); + + return o->type; +} + +int NCDValue_InitString (NCDValue *o, const char *str) +{ + return NCDValue_InitStringBin(o, (const uint8_t *)str, strlen(str)); +} + +int NCDValue_InitStringBin (NCDValue *o, const uint8_t *str, size_t len) +{ + if (len == SIZE_MAX) { + return 0; + } + + if (!(o->string = malloc(len + 1))) { + return 0; + } + + memcpy(o->string, str, len); + o->string[len] = '\0'; + o->string_len = len; + + o->type = NCDVALUE_STRING; + + return 1; +} + +const char * NCDValue_StringValue (NCDValue *o) +{ + ASSERT(o->type == NCDVALUE_STRING) + + return (char *)o->string; +} + +size_t NCDValue_StringLength (NCDValue *o) +{ + ASSERT(o->type == NCDVALUE_STRING) + + return o->string_len; +} + +void NCDValue_InitList (NCDValue *o) +{ + o->type = NCDVALUE_LIST; + LinkedList1_Init(&o->list); + o->list_count = 0; +} + +size_t NCDValue_ListCount (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + return o->list_count; +} + +int NCDValue_ListAppend (NCDValue *o, NCDValue v) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + value_assert(&v); + + if (o->list_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__list_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->v = v; + LinkedList1_Append(&o->list, &e->list_node); + + o->list_count++; + + return 1; +} + +int NCDValue_ListPrepend (NCDValue *o, NCDValue v) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + value_assert(&v); + + if (o->list_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__list_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->v = v; + LinkedList1_Prepend(&o->list, &e->list_node); + + o->list_count++; + + return 1; +} + +NCDValue * NCDValue_ListFirst (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + LinkedList1Node *ln = LinkedList1_GetFirst(&o->list); + + if (!ln) { + return NULL; + } + + struct NCDValue__list_element *e = UPPER_OBJECT(ln, struct NCDValue__list_element, list_node); + + return &e->v; +} + +NCDValue * NCDValue_ListNext (NCDValue *o, NCDValue *ev) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + struct NCDValue__list_element *cur_e = UPPER_OBJECT(ev, struct NCDValue__list_element, v); + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->list_node); + + if (!ln) { + return NULL; + } + + struct NCDValue__list_element *e = UPPER_OBJECT(ln, struct NCDValue__list_element, list_node); + + return &e->v; +} + +void NCDValue_InitMap (NCDValue *o) +{ + o->type = NCDVALUE_MAP; + LinkedList1_Init(&o->map_list); + o->map_count = 0; +} + +size_t NCDValue_MapCount (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + return o->map_count; +} + +int NCDValue_MapPrepend (NCDValue *o, NCDValue key, NCDValue val) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + value_assert(&key); + value_assert(&val); + + if (o->map_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__map_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->key = key; + e->val = val; + LinkedList1_Prepend(&o->map_list, &e->list_node); + + o->map_count++; + + return 1; +} + +NCDValue * NCDValue_MapFirstKey (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + LinkedList1Node *ln = LinkedList1_GetFirst(&o->map_list); + + if (!ln) { + return NULL; + } + + struct NCDValue__map_element *e = UPPER_OBJECT(ln, struct NCDValue__map_element, list_node); + + value_assert(&e->key); + value_assert(&e->val); + + return &e->key; +} + +NCDValue * NCDValue_MapNextKey (NCDValue *o, NCDValue *ekey) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + struct NCDValue__map_element *e0 = UPPER_OBJECT(ekey, struct NCDValue__map_element, key); + value_assert(&e0->key); + value_assert(&e0->val); + + LinkedList1Node *ln = LinkedList1Node_Next(&e0->list_node); + + if (!ln) { + return NULL; + } + + struct NCDValue__map_element *e = UPPER_OBJECT(ln, struct NCDValue__map_element, list_node); + + value_assert(&e->key); + value_assert(&e->val); + + return &e->key; +} + +NCDValue * NCDValue_MapKeyValue (NCDValue *o, NCDValue *ekey) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + struct NCDValue__map_element *e = UPPER_OBJECT(ekey, struct NCDValue__map_element, key); + value_assert(&e->key); + value_assert(&e->val); + + return &e->val; +} + +int NCDValue_InitVar (NCDValue *o, const char *var_name) +{ + ASSERT(var_name) + + if (!(o->var_name = strdup(var_name))) { + return 0; + } + + o->type = NCDVALUE_VAR; + + return 1; +} + +const char * NCDValue_VarName (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_VAR) + + return o->var_name; +} + +void NCDProgram_Init (NCDProgram *o) +{ + LinkedList1_Init(&o->elems_list); + o->num_elems = 0; +} + +void NCDProgram_Free (NCDProgram *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->elems_list)) { + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + NCDProgramElem_Free(&e->elem); + LinkedList1_Remove(&o->elems_list, &e->elems_list_node); + free(e); + } +} + +NCDProgramElem * NCDProgram_PrependElem (NCDProgram *o, NCDProgramElem elem) +{ + if (o->num_elems == SIZE_MAX) { + return NULL; + } + + struct ProgramElem *e = malloc(sizeof(*e)); + if (!e) { + return NULL; + } + + LinkedList1_Prepend(&o->elems_list, &e->elems_list_node); + e->elem = elem; + + o->num_elems++; + + return &e->elem; +} + +NCDProgramElem * NCDProgram_FirstElem (NCDProgram *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->elems_list); + if (!ln) { + return NULL; + } + + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + + return &e->elem; +} + +NCDProgramElem * NCDProgram_NextElem (NCDProgram *o, NCDProgramElem *ee) +{ + ASSERT(ee) + + struct ProgramElem *cur_e = UPPER_OBJECT(ee, struct ProgramElem, elem); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->elems_list_node); + if (!ln) { + return NULL; + } + + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + + return &e->elem; +} + +size_t NCDProgram_NumElems (NCDProgram *o) +{ + return o->num_elems; +} + +int NCDProgram_ContainsElemType (NCDProgram *o, int elem_type) +{ + for (NCDProgramElem *elem = NCDProgram_FirstElem(o); elem; elem = NCDProgram_NextElem(o, elem)) { + if (NCDProgramElem_Type(elem) == elem_type) { + return 1; + } + } + + return 0; +} + +void NCDProgram_RemoveElem (NCDProgram *o, NCDProgramElem *ee) +{ + ASSERT(ee) + + struct ProgramElem *e = UPPER_OBJECT(ee, struct ProgramElem, elem); + NCDProgramElem_Free(&e->elem); + LinkedList1_Remove(&o->elems_list, &e->elems_list_node); + free(e); + + ASSERT(o->num_elems > 0) + o->num_elems--; +} + +int NCDProgram_ReplaceElemWithProgram (NCDProgram *o, NCDProgramElem *ee, NCDProgram replace_prog) +{ + ASSERT(ee) + + if (replace_prog.num_elems > SIZE_MAX - o->num_elems) { + return 0; + } + + struct ProgramElem *e = UPPER_OBJECT(ee, struct ProgramElem, elem); + + LinkedList1_InsertListAfter(&o->elems_list, replace_prog.elems_list, &e->elems_list_node); + o->num_elems += replace_prog.num_elems; + + NCDProgram_RemoveElem(o, ee); + + return 1; +} + +void NCDProgramElem_InitProcess (NCDProgramElem *o, NCDProcess process) +{ + o->type = NCDPROGRAMELEM_PROCESS; + o->process = process; +} + +int NCDProgramElem_InitInclude (NCDProgramElem *o, const char *path_data, size_t path_length) +{ + if (!(o->include.path_data = b_strdup_bin(path_data, path_length))) { + return 0; + } + + o->type = NCDPROGRAMELEM_INCLUDE; + o->include.path_length = path_length; + + return 1; +} + +int NCDProgramElem_InitIncludeGuard (NCDProgramElem *o, const char *id_data, size_t id_length) +{ + if (!(o->include_guard.id_data = b_strdup_bin(id_data, id_length))) { + return 0; + } + + o->type = NCDPROGRAMELEM_INCLUDE_GUARD; + o->include_guard.id_length = id_length; + + return 1; +} + + +void NCDProgramElem_Free (NCDProgramElem *o) +{ + switch (o->type) { + case NCDPROGRAMELEM_PROCESS: { + NCDProcess_Free(&o->process); + } break; + + case NCDPROGRAMELEM_INCLUDE: { + free(o->include.path_data); + } break; + + case NCDPROGRAMELEM_INCLUDE_GUARD: { + free(o->include_guard.id_data); + } break; + + default: ASSERT(0); + } +} + +int NCDProgramElem_Type (NCDProgramElem *o) +{ + return o->type; +} + +NCDProcess * NCDProgramElem_Process (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_PROCESS) + + return &o->process; +} + +const char * NCDProgramElem_IncludePathData (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE) + + return o->include.path_data; +} + +size_t NCDProgramElem_IncludePathLength (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE) + + return o->include.path_length; +} + +const char * NCDProgramElem_IncludeGuardIdData (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE_GUARD) + + return o->include_guard.id_data; +} + +size_t NCDProgramElem_IncludeGuardIdLength (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE_GUARD) + + return o->include_guard.id_length; +} + +int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block) +{ + ASSERT(is_template == !!is_template) + ASSERT(name) + + if (!(o->name = strdup(name))) { + return 0; + } + + o->is_template = is_template; + o->block = block; + + return 1; +} + +void NCDProcess_Free (NCDProcess *o) +{ + NCDBlock_Free(&o->block); + free(o->name); +} + +int NCDProcess_IsTemplate (NCDProcess *o) +{ + return o->is_template; +} + +const char * NCDProcess_Name (NCDProcess *o) +{ + return o->name; +} + +NCDBlock * NCDProcess_Block (NCDProcess *o) +{ + return &o->block; +} + +void NCDBlock_Init (NCDBlock *o) +{ + LinkedList1_Init(&o->statements_list); + o->count = 0; +} + +void NCDBlock_Free (NCDBlock *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->statements_list)) { + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + NCDStatement_Free(&e->s); + LinkedList1_Remove(&o->statements_list, &e->statements_list_node); + free(e); + } +} + +int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s) +{ + return NCDBlock_InsertStatementAfter(o, NULL, s); +} + +int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s) +{ + struct BlockStatement *after_e = NULL; + if (after) { + after_e = UPPER_OBJECT(after, struct BlockStatement, s); + } + + if (o->count == SIZE_MAX) { + return 0; + } + + struct BlockStatement *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + if (after_e) { + LinkedList1_InsertAfter(&o->statements_list, &e->statements_list_node, &after_e->statements_list_node); + } else { + LinkedList1_Prepend(&o->statements_list, &e->statements_list_node); + } + e->s = s; + + o->count++; + + return 1; +} + +NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s) +{ + ASSERT(es) + + struct BlockStatement *e = UPPER_OBJECT(es, struct BlockStatement, s); + + NCDStatement_Free(&e->s); + e->s = s; + + return &e->s; +} + +NCDStatement * NCDBlock_FirstStatement (NCDBlock *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->statements_list); + if (!ln) { + return NULL; + } + + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + + return &e->s; +} + +NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es) +{ + ASSERT(es) + + struct BlockStatement *cur_e = UPPER_OBJECT(es, struct BlockStatement, s); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->statements_list_node); + if (!ln) { + return NULL; + } + + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + + return &e->s; +} + +size_t NCDBlock_NumStatements (NCDBlock *o) +{ + return o->count; +} + +int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args) +{ + ASSERT(cmdname) + ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST) + + o->name = NULL; + o->reg.objname = NULL; + o->reg.cmdname = NULL; + + if (name && !(o->name = strdup(name))) { + goto fail; + } + + if (objname && !(o->reg.objname = strdup(objname))) { + goto fail; + } + + if (!(o->reg.cmdname = strdup(cmdname))) { + goto fail; + } + + o->type = NCDSTATEMENT_REG; + o->reg.args = args; + + return 1; + +fail: + free(o->name); + free(o->reg.objname); + free(o->reg.cmdname); + return 0; +} + +int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock) +{ + o->name = NULL; + + if (name && !(o->name = strdup(name))) { + return 0; + } + + o->type = NCDSTATEMENT_IF; + o->ifc.ifblock = ifblock; + o->ifc.have_else = 0; + + return 1; +} + +int NCDStatement_InitForeach (NCDStatement *o, const char *name, NCDValue collection, const char *name1, const char *name2, NCDBlock block) +{ + ASSERT(name1) + + o->name = NULL; + o->foreach.name1 = NULL; + o->foreach.name2 = NULL; + + if (name && !(o->name = strdup(name))) { + goto fail; + } + + if (!(o->foreach.name1 = strdup(name1))) { + goto fail; + } + + if (name2 && !(o->foreach.name2 = strdup(name2))) { + goto fail; + } + + o->type = NCDSTATEMENT_FOREACH; + o->foreach.collection = collection; + o->foreach.block = block; + o->foreach.is_grabbed = 0; + + return 1; + +fail: + free(o->name); + free(o->foreach.name1); + free(o->foreach.name2); + return 0; +} + +void NCDStatement_Free (NCDStatement *o) +{ + switch (o->type) { + case NCDSTATEMENT_REG: { + NCDValue_Free(&o->reg.args); + free(o->reg.cmdname); + free(o->reg.objname); + } break; + + case NCDSTATEMENT_IF: { + if (o->ifc.have_else) { + NCDBlock_Free(&o->ifc.else_block); + } + + NCDIfBlock_Free(&o->ifc.ifblock); + } break; + + case NCDSTATEMENT_FOREACH: { + if (!o->foreach.is_grabbed) { + NCDBlock_Free(&o->foreach.block); + NCDValue_Free(&o->foreach.collection); + } + free(o->foreach.name2); + free(o->foreach.name1); + } break; + + default: ASSERT(0); + } + + free(o->name); +} + +int NCDStatement_Type (NCDStatement *o) +{ + return o->type; +} + +const char * NCDStatement_Name (NCDStatement *o) +{ + return o->name; +} + +const char * NCDStatement_RegObjName (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return o->reg.objname; +} + +const char * NCDStatement_RegCmdName (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return o->reg.cmdname; +} + +NCDValue * NCDStatement_RegArgs (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return &o->reg.args; +} + +NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + + return &o->ifc.ifblock; +} + +void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + ASSERT(!o->ifc.have_else) + + o->ifc.have_else = 1; + o->ifc.else_block = else_block; +} + +NCDBlock * NCDStatement_IfElse (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + + if (!o->ifc.have_else) { + return NULL; + } + + return &o->ifc.else_block; +} + +NCDBlock NCDStatement_IfGrabElse (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + ASSERT(o->ifc.have_else) + + o->ifc.have_else = 0; + + return o->ifc.else_block; +} + +NCDValue * NCDStatement_ForeachCollection (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + return &o->foreach.collection; +} + +const char * NCDStatement_ForeachName1 (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + + return o->foreach.name1; +} + +const char * NCDStatement_ForeachName2 (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + + return o->foreach.name2; +} + +NCDBlock * NCDStatement_ForeachBlock (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + return &o->foreach.block; +} + +void NCDStatement_ForeachGrab (NCDStatement *o, NCDValue *out_collection, NCDBlock *out_block) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + *out_collection = o->foreach.collection; + *out_block = o->foreach.block; + o->foreach.is_grabbed = 1; +} + +void NCDIfBlock_Init (NCDIfBlock *o) +{ + LinkedList1_Init(&o->ifs_list); +} + +void NCDIfBlock_Free (NCDIfBlock *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->ifs_list)) { + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + NCDIf_Free(&e->ifc); + LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node); + free(e); + } +} + +int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc) +{ + struct IfBlockIf *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + LinkedList1_Prepend(&o->ifs_list, &e->ifs_list_node); + e->ifc = ifc; + + return 1; +} + +NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->ifs_list); + if (!ln) { + return NULL; + } + + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + + return &e->ifc; +} + +NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei) +{ + ASSERT(ei) + + struct IfBlockIf *cur_e = UPPER_OBJECT(ei, struct IfBlockIf, ifc); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->ifs_list_node); + if (!ln) { + return NULL; + } + + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + + return &e->ifc; +} + +NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei) +{ + ASSERT(ei) + + struct IfBlockIf *e = UPPER_OBJECT(ei, struct IfBlockIf, ifc); + + NCDIf old_ifc = e->ifc; + + LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node); + free(e); + + return old_ifc; +} + +void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block) +{ + o->cond = cond; + o->block = block; +} + +void NCDIf_Free (NCDIf *o) +{ + NCDValue_Free(&o->cond); + NCDBlock_Free(&o->block); +} + +void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block) +{ + *out_cond = o->cond; + *out_block = o->block; +} + +NCDValue * NCDIf_Cond (NCDIf *o) +{ + return &o->cond; +} + +NCDBlock * NCDIf_Block (NCDIf *o) +{ + return &o->block; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDAst.h b/app/src/main/jni/badvpn/ncd/NCDAst.h new file mode 100644 index 0000000..95ca9e4 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDAst.h @@ -0,0 +1,237 @@ +/** + * @file NCDAst.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDAST_H +#define BADVPN_NCDAST_H + +#include +#include + +#include +#include + +typedef struct NCDValue_s NCDValue; +typedef struct NCDProgram_s NCDProgram; +typedef struct NCDProgramElem_s NCDProgramElem; +typedef struct NCDProcess_s NCDProcess; +typedef struct NCDBlock_s NCDBlock; +typedef struct NCDStatement_s NCDStatement; +typedef struct NCDIfBlock_s NCDIfBlock; +typedef struct NCDIf_s NCDIf; + +struct NCDValue_s { + int type; + union { + struct { + uint8_t *string; + size_t string_len; + }; + struct { + LinkedList1 list; + size_t list_count; + }; + struct { + LinkedList1 map_list; + size_t map_count; + }; + struct { + char *var_name; + }; + }; +}; + +struct NCDProgram_s { + LinkedList1 elems_list; + size_t num_elems; +}; + +struct NCDBlock_s { + LinkedList1 statements_list; + size_t count; +}; + +struct NCDProcess_s { + int is_template; + char *name; + NCDBlock block; +}; + +struct NCDProgramElem_s { + int type; + union { + NCDProcess process; + struct { + char *path_data; + size_t path_length; + } include; + struct { + char *id_data; + size_t id_length; + } include_guard; + }; +}; + +struct NCDIfBlock_s { + LinkedList1 ifs_list; +}; + +struct NCDStatement_s { + int type; + char *name; + union { + struct { + char *objname; + char *cmdname; + NCDValue args; + } reg; + struct { + NCDIfBlock ifblock; + int have_else; + NCDBlock else_block; + } ifc; + struct { + NCDValue collection; + char *name1; + char *name2; + NCDBlock block; + int is_grabbed; + } foreach; + }; +}; + +struct NCDIf_s { + NCDValue cond; + NCDBlock block; +}; + +// + +#define NCDVALUE_STRING 1 +#define NCDVALUE_LIST 2 +#define NCDVALUE_MAP 3 +#define NCDVALUE_VAR 4 + +#define NCDPROGRAMELEM_PROCESS 1 +#define NCDPROGRAMELEM_INCLUDE 2 +#define NCDPROGRAMELEM_INCLUDE_GUARD 3 + +#define NCDSTATEMENT_REG 1 +#define NCDSTATEMENT_IF 2 +#define NCDSTATEMENT_FOREACH 3 + +void NCDValue_Free (NCDValue *o); +int NCDValue_Type (NCDValue *o); +int NCDValue_InitString (NCDValue *o, const char *str) WARN_UNUSED; +int NCDValue_InitStringBin (NCDValue *o, const uint8_t *str, size_t len) WARN_UNUSED; +const char * NCDValue_StringValue (NCDValue *o); +size_t NCDValue_StringLength (NCDValue *o); +void NCDValue_InitList (NCDValue *o); +size_t NCDValue_ListCount (NCDValue *o); +int NCDValue_ListAppend (NCDValue *o, NCDValue v) WARN_UNUSED; +int NCDValue_ListPrepend (NCDValue *o, NCDValue v) WARN_UNUSED; +NCDValue * NCDValue_ListFirst (NCDValue *o); +NCDValue * NCDValue_ListNext (NCDValue *o, NCDValue *ev); +void NCDValue_InitMap (NCDValue *o); +size_t NCDValue_MapCount (NCDValue *o); +int NCDValue_MapPrepend (NCDValue *o, NCDValue key, NCDValue val) WARN_UNUSED; +NCDValue * NCDValue_MapFirstKey (NCDValue *o); +NCDValue * NCDValue_MapNextKey (NCDValue *o, NCDValue *ekey); +NCDValue * NCDValue_MapKeyValue (NCDValue *o, NCDValue *ekey); +int NCDValue_InitVar (NCDValue *o, const char *var_name) WARN_UNUSED; +const char * NCDValue_VarName (NCDValue *o); + +void NCDProgram_Init (NCDProgram *o); +void NCDProgram_Free (NCDProgram *o); +NCDProgramElem * NCDProgram_PrependElem (NCDProgram *o, NCDProgramElem elem) WARN_UNUSED; +NCDProgramElem * NCDProgram_FirstElem (NCDProgram *o); +NCDProgramElem * NCDProgram_NextElem (NCDProgram *o, NCDProgramElem *ee); +size_t NCDProgram_NumElems (NCDProgram *o); +int NCDProgram_ContainsElemType (NCDProgram *o, int elem_type); +void NCDProgram_RemoveElem (NCDProgram *o, NCDProgramElem *ee); +int NCDProgram_ReplaceElemWithProgram (NCDProgram *o, NCDProgramElem *ee, NCDProgram replace_prog) WARN_UNUSED; + +void NCDProgramElem_InitProcess (NCDProgramElem *o, NCDProcess process); +int NCDProgramElem_InitInclude (NCDProgramElem *o, const char *path_data, size_t path_length) WARN_UNUSED; +int NCDProgramElem_InitIncludeGuard (NCDProgramElem *o, const char *id_data, size_t id_length) WARN_UNUSED; +void NCDProgramElem_Free (NCDProgramElem *o); +int NCDProgramElem_Type (NCDProgramElem *o); +NCDProcess * NCDProgramElem_Process (NCDProgramElem *o); +const char * NCDProgramElem_IncludePathData (NCDProgramElem *o); +size_t NCDProgramElem_IncludePathLength (NCDProgramElem *o); +const char * NCDProgramElem_IncludeGuardIdData (NCDProgramElem *o); +size_t NCDProgramElem_IncludeGuardIdLength (NCDProgramElem *o); + +int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block) WARN_UNUSED; +void NCDProcess_Free (NCDProcess *o); +int NCDProcess_IsTemplate (NCDProcess *o); +const char * NCDProcess_Name (NCDProcess *o); +NCDBlock * NCDProcess_Block (NCDProcess *o); + +void NCDBlock_Init (NCDBlock *o); +void NCDBlock_Free (NCDBlock *o); +int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s) WARN_UNUSED; +int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s) WARN_UNUSED; +NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s); +NCDStatement * NCDBlock_FirstStatement (NCDBlock *o); +NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es); +size_t NCDBlock_NumStatements (NCDBlock *o); + +int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args) WARN_UNUSED; +int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock) WARN_UNUSED; +int NCDStatement_InitForeach (NCDStatement *o, const char *name, NCDValue collection, const char *name1, const char *name2, NCDBlock block) WARN_UNUSED; +void NCDStatement_Free (NCDStatement *o); +int NCDStatement_Type (NCDStatement *o); +const char * NCDStatement_Name (NCDStatement *o); +const char * NCDStatement_RegObjName (NCDStatement *o); +const char * NCDStatement_RegCmdName (NCDStatement *o); +NCDValue * NCDStatement_RegArgs (NCDStatement *o); +NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o); +void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block); +NCDBlock * NCDStatement_IfElse (NCDStatement *o); +NCDBlock NCDStatement_IfGrabElse (NCDStatement *o); +NCDValue * NCDStatement_ForeachCollection (NCDStatement *o); +const char * NCDStatement_ForeachName1 (NCDStatement *o); +const char * NCDStatement_ForeachName2 (NCDStatement *o); +NCDBlock * NCDStatement_ForeachBlock (NCDStatement *o); +void NCDStatement_ForeachGrab (NCDStatement *o, NCDValue *out_collection, NCDBlock *out_block); + +void NCDIfBlock_Init (NCDIfBlock *o); +void NCDIfBlock_Free (NCDIfBlock *o); +int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc) WARN_UNUSED; +NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o); +NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei); +NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei); + +void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block); +void NCDIf_Free (NCDIf *o); +void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block); +NCDValue * NCDIf_Cond (NCDIf *o); +NCDBlock * NCDIf_Block (NCDIf *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDBuildProgram.c b/app/src/main/jni/badvpn/ncd/NCDBuildProgram.c new file mode 100644 index 0000000..941a142 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDBuildProgram.c @@ -0,0 +1,316 @@ +/** + * @file NCDBuildProgram.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NCDBuildProgram.h" + +#include + +#define MAX_INCLUDE_DEPTH 32 + +struct guard { + char *id_data; + size_t id_length; + struct guard *next; +}; + +struct build_state { + struct guard *top_guard; +}; + +static int add_guard (struct guard **first, const char *id_data, size_t id_length) +{ + struct guard *g = malloc(sizeof(*g)); + if (!g) { + goto fail0; + } + + if (!(g->id_data = b_strdup_bin(id_data, id_length))) { + goto fail1; + } + + g->id_length = id_length; + + g->next = *first; + *first = g; + + return 1; + +fail1: + free(g); +fail0: + return 0; +} + +static void prepend_guards (struct guard **first, struct guard *guards) +{ + if (!guards) { + return; + } + + struct guard *last = guards; + while (last->next) { + last = last->next; + } + + last->next = *first; + *first = guards; +} + +static void free_guards (struct guard *g) +{ + while (g) { + struct guard *next_g = g->next; + free(g->id_data); + free(g); + g = next_g; + } +} + +static int guard_exists (struct guard *top_guard, const char *id_data, size_t id_length) +{ + for (struct guard *g = top_guard; g; g = g->next) { + if (g->id_length == id_length && !memcmp(g->id_data, id_data, id_length)) { + return 1; + } + } + + return 0; +} + +static char * make_dir_path (const char *file_path) +{ + int found_slash = 0; + size_t last_slash = 0; // initialize to remove warning + + for (size_t i = 0; file_path[i]; i++) { + if (file_path[i] == '/') { + found_slash = 1; + last_slash = i; + } + } + + char *dir_path; + + if (!found_slash) { + if (!file_path[0]) { + BLog(BLOG_ERROR, "file '%s': file path must not be empty", file_path); + return NULL; + } + dir_path = b_strdup(""); + } else { + if (!file_path[last_slash + 1]) { + BLog(BLOG_ERROR, "file '%s': file path must not end in a slash", file_path); + return NULL; + } + dir_path = b_strdup_bin(file_path, last_slash + 1); + } + + if (!dir_path) { + BLog(BLOG_ERROR, "file '%s': b_strdup/b_strdup_bin failed", file_path); + return NULL; + } + + return dir_path; +} + +static char * make_include_path (const char *file_path, const char *dir_path, const char *target, size_t target_len) +{ + ASSERT(target_len == strlen(target)) + + if (target_len == 0) { + BLog(BLOG_ERROR, "file '%s': include target must not be empty", file_path); + return NULL; + } + + if (target[target_len - 1] == '/') { + BLog(BLOG_ERROR, "file '%s': include target must not end in a slash", file_path); + return NULL; + } + + char *real_target; + + if (target[0] == '/') { + real_target = b_strdup(target); + } else { + real_target = concat_strings(2, dir_path, target); + } + + if (!real_target) { + BLog(BLOG_ERROR, "file '%s': b_strdup/concat_strings failed", file_path); + return NULL; + } + + return real_target; +} + +static int process_file (struct build_state *st, int depth, const char *file_path, NCDProgram *out_program, int *out_guarded) +{ + int ret_val = 0; + + if (depth > MAX_INCLUDE_DEPTH) { + BLog(BLOG_ERROR, "file '%s': maximum include depth (%d) exceeded (include cycle?)", file_path, (int)MAX_INCLUDE_DEPTH); + goto fail0; + } + + char *dir_path = make_dir_path(file_path); + if (!dir_path) { + goto fail0; + } + + uint8_t *data; + size_t len; + if (!read_file(file_path, &data, &len)) { + BLog(BLOG_ERROR, "file '%s': failed to read contents", file_path); + goto fail1; + } + + NCDProgram program; + int res = NCDConfigParser_Parse((char *)data, len, &program); + free(data); + if (!res) { + BLog(BLOG_ERROR, "file '%s': failed to parse", file_path); + goto fail1; + } + + struct guard *our_guards = NULL; + + NCDProgramElem *elem = NCDProgram_FirstElem(&program); + while (elem) { + NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem); + if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE_GUARD) { + elem = next_elem; + continue; + } + + const char *id_data = NCDProgramElem_IncludeGuardIdData(elem); + size_t id_length = NCDProgramElem_IncludeGuardIdLength(elem); + + if (guard_exists(st->top_guard, id_data, id_length)) { + *out_guarded = 1; + ret_val = 1; + goto fail2; + } + + if (!add_guard(&our_guards, id_data, id_length)) { + BLog(BLOG_ERROR, "file '%s': add_guard failed", file_path); + goto fail2; + } + + NCDProgram_RemoveElem(&program, elem); + elem = next_elem; + } + + prepend_guards(&st->top_guard, our_guards); + our_guards = NULL; + + elem = NCDProgram_FirstElem(&program); + while (elem) { + NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem); + if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE) { + elem = next_elem; + continue; + } + + const char *target = NCDProgramElem_IncludePathData(elem); + size_t target_len = NCDProgramElem_IncludePathLength(elem); + + if (strlen(target) != target_len) { + BLog(BLOG_ERROR, "file '%s': include path must not contain null characters", file_path); + goto fail2; + } + + char *real_target = make_include_path(file_path, dir_path, target, target_len); + if (!real_target) { + goto fail2; + } + + NCDProgram included_program; + int included_guarded; + int res = process_file(st, depth + 1, real_target, &included_program, &included_guarded); + free(real_target); + if (!res) { + goto fail2; + } + + if (included_guarded) { + NCDProgram_RemoveElem(&program, elem); + } else { + if (!NCDProgram_ReplaceElemWithProgram(&program, elem, included_program)) { + BLog(BLOG_ERROR, "file '%s': NCDProgram_ReplaceElemWithProgram failed", file_path); + NCDProgram_Free(&included_program); + goto fail2; + } + } + + elem = next_elem; + } + + free(dir_path); + + *out_program = program; + *out_guarded = 0; + return 1; + +fail2: + free_guards(our_guards); + NCDProgram_Free(&program); +fail1: + free(dir_path); +fail0: + return ret_val; +} + +int NCDBuildProgram_Build (const char *file_path, NCDProgram *out_program) +{ + ASSERT(file_path) + ASSERT(out_program) + + struct build_state st; + st.top_guard = NULL; + + int guarded; + int res = process_file(&st, 0, file_path, out_program, &guarded); + + ASSERT(!res || !guarded) + + free_guards(st.top_guard); + + return res; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDBuildProgram.h b/app/src/main/jni/badvpn/ncd/NCDBuildProgram.h new file mode 100644 index 0000000..dff6685 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDBuildProgram.h @@ -0,0 +1,49 @@ +/** + * @file NCDBuildProgram.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BUILD_PROGRAM_H +#define NCD_BUILD_PROGRAM_H + +#include +#include + +/** + * Builds an NCD program in AST form suitable for passing to {@link NCDInterpreter}, + * by opening and parsing it, as well as recursively processing any included files. + * The resulting program will not contain any 'include' or 'include_guard' elements; + * these will be resolved and removed. + * + * @param file_path path to the main file of the program + * @param out_program on success, *out_program will contain the resulting program. + * On failure, *out_program will be unchanged. + * @return 1 on success, 0 on failure + */ +int NCDBuildProgram_Build (const char *file_path, NCDProgram *out_program) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDConfigParser.c b/app/src/main/jni/badvpn/ncd/NCDConfigParser.c new file mode 100644 index 0000000..f883f72 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDConfigParser.c @@ -0,0 +1,214 @@ +/** + * @file NCDConfigParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "ncd/NCDConfigParser.h" + +#include + +#include "../generated/NCDConfigParser_parse.c" +#include "../generated/NCDConfigParser_parse.h" + +struct parser_state { + struct parser_out out; + int error; + void *parser; +}; + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + struct parser_state *state = user; + ASSERT(!state->out.out_of_memory) + ASSERT(!state->out.syntax_error) + ASSERT(!state->error) + + if (token == NCD_ERROR) { + BLog(BLOG_ERROR, "line %zu, character %zu: tokenizer error", line, line_char); + state->error = 1; + return 0; + } + + struct token minor; + minor.str = value; + minor.len = value_len; + + switch (token) { + case NCD_EOF: { + Parse(state->parser, 0, minor, &state->out); + } break; + + case NCD_TOKEN_CURLY_OPEN: { + Parse(state->parser, CURLY_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_CURLY_CLOSE: { + Parse(state->parser, CURLY_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_ROUND_OPEN: { + Parse(state->parser, ROUND_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_ROUND_CLOSE: { + Parse(state->parser, ROUND_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_SEMICOLON: { + Parse(state->parser, SEMICOLON, minor, &state->out); + } break; + + case NCD_TOKEN_DOT: { + Parse(state->parser, DOT, minor, &state->out); + } break; + + case NCD_TOKEN_COMMA: { + Parse(state->parser, COMMA, minor, &state->out); + } break; + + case NCD_TOKEN_ARROW: { + Parse(state->parser, ARROW, minor, &state->out); + } break; + + case NCD_TOKEN_PROCESS: { + Parse(state->parser, PROCESS, minor, &state->out); + } break; + + case NCD_TOKEN_TEMPLATE: { + Parse(state->parser, TEMPLATE, minor, &state->out); + } break; + + case NCD_TOKEN_NAME: { + Parse(state->parser, NAME, minor, &state->out); + } break; + + case NCD_TOKEN_STRING: { + Parse(state->parser, STRING, minor, &state->out); + } break; + + case NCD_TOKEN_COLON: { + Parse(state->parser, COLON, minor, &state->out); + } break; + + case NCD_TOKEN_BRACKET_OPEN: { + Parse(state->parser, BRACKET_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_BRACKET_CLOSE: { + Parse(state->parser, BRACKET_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_IF: { + Parse(state->parser, IF, minor, &state->out); + } break; + + case NCD_TOKEN_ELIF: { + Parse(state->parser, ELIF, minor, &state->out); + } break; + + case NCD_TOKEN_ELSE: { + Parse(state->parser, ELSE, minor, &state->out); + } break; + + case NCD_TOKEN_FOREACH: { + Parse(state->parser, FOREACH, minor, &state->out); + } break; + + case NCD_TOKEN_AS: { + Parse(state->parser, AS, minor, &state->out); + } break; + + case NCD_TOKEN_INCLUDE: { + Parse(state->parser, INCLUDE, minor, &state->out); + } break; + + case NCD_TOKEN_INCLUDE_GUARD: { + Parse(state->parser, INCLUDE_GUARD, minor, &state->out); + } break; + + default: + BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char); + free(minor.str); + state->error = 1; + return 0; + } + + // if we got syntax error, stop parsing + if (state->out.syntax_error) { + BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char); + state->error = 1; + return 0; + } + + if (state->out.out_of_memory) { + BLog(BLOG_ERROR, "line %zu, character %zu: out of memory", line, line_char); + state->error = 1; + return 0; + } + + return 1; +} + +int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast) +{ + struct parser_state state; + + state.out.out_of_memory = 0; + state.out.syntax_error = 0; + state.out.have_ast = 0; + state.error = 0; + + if (!(state.parser = ParseAlloc(malloc))) { + BLog(BLOG_ERROR, "ParseAlloc failed"); + return 0; + } + + // tokenize and parse + NCDConfigTokenizer_Tokenize(config, config_len, tokenizer_output, &state); + + ParseFree(state.parser, free); + + if (state.error) { + if (state.out.have_ast) { + NCDProgram_Free(&state.out.ast); + } + return 0; + } + + ASSERT(state.out.have_ast) + + *out_ast = state.out.ast; + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDConfigParser.h b/app/src/main/jni/badvpn/ncd/NCDConfigParser.h new file mode 100644 index 0000000..90cee54 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDConfigParser.h @@ -0,0 +1,40 @@ +/** + * @file NCDConfigParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDCONFIG_NCDCONFIGPARSER_H +#define BADVPN_NCDCONFIG_NCDCONFIGPARSER_H + +#include + +#include +#include + +int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDConfigParser_parse.y b/app/src/main/jni/badvpn/ncd/NCDConfigParser_parse.y new file mode 100644 index 0000000..fdf89f6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDConfigParser_parse.y @@ -0,0 +1,718 @@ +/** + * @file NCDConfigParser.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%include { + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +} + +%extra_argument { struct parser_out *parser_out } + +%token_type { struct token } + +%token_destructor { free_token($$); } + +%type processes { struct program } +%type statement { struct statement } +%type elif_maybe { struct ifblock } +%type elif { struct ifblock } +%type else_maybe { struct block } +%type statements { struct block } +%type dotted_name { char * } +%type statement_args_maybe { struct value } +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } +%type name_maybe { char * } +%type process_or_template { int } + +// mention parser_out in some destructor to a void unused variable warning +%destructor processes { (void)parser_out; free_program($$); } +%destructor statement { free_statement($$); } +%destructor elif_maybe { free_ifblock($$); } +%destructor elif { free_ifblock($$); } +%destructor else_maybe { free_block($$); } +%destructor statements { free_block($$); } +%destructor dotted_name { free($$); } +%destructor statement_args_maybe { free_value($$); } +%destructor list_contents { free_value($$); } +%destructor list { free_value($$); } +%destructor map_contents { free_value($$); } +%destructor map { free_value($$); } +%destructor value { free_value($$); } +%destructor name_maybe { free($$); } + +%stack_size 0 + +%syntax_error { + parser_out->syntax_error = 1; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= processes(A). { + ASSERT(!parser_out->have_ast) + + if (A.have) { + parser_out->have_ast = 1; + parser_out->ast = A.v; + } +} + +processes(R) ::= . { + NCDProgram prog; + NCDProgram_Init(&prog); + + R.have = 1; + R.v = prog; +} + +processes(R) ::= INCLUDE STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, A.str, A.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failA1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(A); + free_program(N); +} + +processes(R) ::= INCLUDE_GUARD STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, A.str, A.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failZ1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + R.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(A); + free_program(N); +} + +processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). { + ASSERT(A.str) + if (!B.have || !N.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, T, A.str, B.v)) { + goto failB0; + } + B.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failB1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(A); + free_block(B); + free_program(N); +} + +statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!A || !B.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) { + goto failC0; + } + B.have = 0; + + R.have = 1; + goto doneC; + +failC0: + R.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(A); + free_value(B); + free(C); +} + +statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!M || !A || !B.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) { + goto failD0; + } + B.have = 0; + + R.have = 1; + goto doneD; + +failD0: + R.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(M); + free(A); + free_value(B); + free(C); +} + +statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. { + if (!A.have || !B.have || !I.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&I.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&R.v, C, I.v)) { + goto failE0; + } + I.have = 0; + + if (E.have) { + NCDStatement_IfAddElse(&R.v, E.v); + E.have = 0; + } + + R.have = 1; + goto doneE; + +failE0: + R.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(A); + free_block(B); + free_ifblock(I); + free_block(E); + free(C); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !S.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, NULL, S.v)) { + goto failEA0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEA0; + +failEA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(A); + free_token(B); + free_block(S); + free(N); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) COLON NAME(C) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !C.str || !S.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, C.str, S.v)) { + goto failEB0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEB0; + +failEB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(A); + free_token(B); + free_token(C); + free_block(S); + free(N); +} + +elif_maybe(R) ::= . { + NCDIfBlock_Init(&R.v); + R.have = 1; +} + +elif_maybe(R) ::= elif(A). { + R = A; +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. { + if (!A.have || !B.have) { + goto failF0; + } + + NCDIfBlock_Init(&R.v); + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&R.v, ifc)) { + goto failF1; + } + + R.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&R.v); +failF0: + R.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(A); + free_block(B); +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). { + if (!A.have || !B.have || !N.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&N.v, ifc)) { + goto failG1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + R.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(A); + free_block(B); + free_ifblock(N); +} + +else_maybe(R) ::= . { + R.have = 0; +} + +else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. { + R = B; +} + +statements(R) ::= statement(A). { + if (!A.have) { + goto failH0; + } + + NCDBlock_Init(&R.v); + + if (!NCDBlock_PrependStatement(&R.v, A.v)) { + goto failH1; + } + A.have = 0; + + R.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&R.v); +failH0: + R.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(A); +} + +statements(R) ::= statement(A) statements(N). { + if (!A.have || !N.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&N.v, A.v)) { + goto failI1; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&R.v); +failI0: + R.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(A); + free_block(N); +} + +dotted_name(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +dotted_name(R) ::= NAME(A) DOT dotted_name(N). { + ASSERT(A.str) + if (!N) { + goto failJ0; + } + + if (!(R = concat_strings(3, A.str, ".", N))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + R = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(A); + free(N); +} + +statement_args_maybe(R) ::= . { + R.have = 1; + NCDValue_InitList(&R.v); +} + +statement_args_maybe(R) ::= list_contents(A). { + R = A; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValue_InitList(&R.v); + + if (!NCDValue_ListPrepend(&R.v, A.v)) { + goto failL1; + } + A.have = 0; + + R.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&R.v); +failL0: + R.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(A); +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&N.v, A.v)) { + goto failM0; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneM; + +failM0: + R.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(A); + free_value(N); +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + R.have = 1; + NCDValue_InitList(&R.v); +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValue_InitMap(&R.v); + + if (!NCDValue_MapPrepend(&R.v, A.v, B.v)) { + goto failS1; + } + A.have = 0; + B.have = 0; + + R.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&R.v); +failS0: + R.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(A); + free_value(B); +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&N.v, A.v, B.v)) { + goto failT0; + } + A.have = 0; + B.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneT; + +failT0: + R.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(A); + free_value(B); + free_value(N); +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + R.have = 1; + NCDValue_InitMap(&R.v); +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) { + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(A); +} + +value(R) ::= dotted_name(A). { + if (!A) { + goto failV0; + } + + if (!NCDValue_InitVar(&R.v, A)) { + goto failV0; + } + + R.have = 1; + goto doneV; + +failV0: + R.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} + +name_maybe(R) ::= . { + R = NULL; +} + +name_maybe(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +process_or_template(R) ::= PROCESS. { + R = 0; +} + +process_or_template(R) ::= TEMPLATE. { + R = 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.c b/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.c new file mode 100644 index 0000000..5ba31b4 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.c @@ -0,0 +1,321 @@ +/** + * @file NCDConfigTokenizer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int is_name_char (char c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'); +} + +static int is_name_first_char (char c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'); +} + +static int is_space_char (char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static int string_equals (char *str, int str_len, char *needle) +{ + return (str_len == strlen(needle) && !memcmp(str, needle, str_len)); +} + +void NCDConfigTokenizer_Tokenize (char *str, size_t left, NCDConfigTokenizer_output output, void *user) +{ + size_t line = 1; + size_t line_char = 1; + + while (left > 0) { + size_t l; + int error = 0; + int token; + void *token_val = NULL; + size_t token_len = 0; + + if (*str == '#') { + l = 1; + while (l < left && str[l] != '\n') { + l++; + } + token = 0; + } + else if (l = data_begins_with(str, left, "{")) { + token = NCD_TOKEN_CURLY_OPEN; + } + else if (l = data_begins_with(str, left, "}")) { + token = NCD_TOKEN_CURLY_CLOSE; + } + else if (l = data_begins_with(str, left, "(")) { + token = NCD_TOKEN_ROUND_OPEN; + } + else if (l = data_begins_with(str, left, ")")) { + token = NCD_TOKEN_ROUND_CLOSE; + } + else if (l = data_begins_with(str, left, ";")) { + token = NCD_TOKEN_SEMICOLON; + } + else if (l = data_begins_with(str, left, ".")) { + token = NCD_TOKEN_DOT; + } + else if (l = data_begins_with(str, left, ",")) { + token = NCD_TOKEN_COMMA; + } + else if (l = data_begins_with(str, left, ":")) { + token = NCD_TOKEN_COLON; + } + else if (l = data_begins_with(str, left, "[")) { + token = NCD_TOKEN_BRACKET_OPEN; + } + else if (l = data_begins_with(str, left, "]")) { + token = NCD_TOKEN_BRACKET_CLOSE; + } + else if (l = data_begins_with(str, left, "->")) { + token = NCD_TOKEN_ARROW; + } + else if (l = data_begins_with(str, left, "If")) { + token = NCD_TOKEN_IF; + } + else if (l = data_begins_with(str, left, "Elif")) { + token = NCD_TOKEN_ELIF; + } + else if (l = data_begins_with(str, left, "elif")) { + token = NCD_TOKEN_ELIF; + } + else if (l = data_begins_with(str, left, "Else")) { + token = NCD_TOKEN_ELSE; + } + else if (l = data_begins_with(str, left, "else")) { + token = NCD_TOKEN_ELSE; + } + else if (l = data_begins_with(str, left, "Foreach")) { + token = NCD_TOKEN_FOREACH; + } + else if (l = data_begins_with(str, left, "As")) { + token = NCD_TOKEN_AS; + } + else if (l = data_begins_with(str, left, "include_guard")) { + token = NCD_TOKEN_INCLUDE_GUARD; + } + else if (l = data_begins_with(str, left, "include")) { + token = NCD_TOKEN_INCLUDE; + } + else if (is_name_first_char(*str)) { + l = 1; + while (l < left && is_name_char(str[l])) { + l++; + } + + // allocate buffer + bsize_t bufsize = bsize_add(bsize_fromsize(l), bsize_fromint(1)); + char *buf; + if (bufsize.is_overflow || !(buf = malloc(bufsize.value))) { + BLog(BLOG_ERROR, "malloc failed"); + error = 1; + goto out; + } + + // copy and terminate + memcpy(buf, str, l); + buf[l] = '\0'; + + if (!strcmp(buf, "process")) { + token = NCD_TOKEN_PROCESS; + free(buf); + } + else if (!strcmp(buf, "template")) { + token = NCD_TOKEN_TEMPLATE; + free(buf); + } + else { + token = NCD_TOKEN_NAME; + token_val = buf; + token_len = l; + } + } + else if (*str == '"') do { + // init string + ExpString estr; + if (!ExpString_Init(&estr)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto string_fail0; + } + + // skip start quote + l = 1; + + // decode string + while (l < left) { + uint8_t dec_ch; + + // get character + if (str[l] == '\\') { + if (left - l < 2) { + BLog(BLOG_ERROR, "escape character found in string but nothing follows"); + goto string_fail1; + } + + size_t extra = 0; + + switch (str[l + 1]) { + case '\'': + case '\"': + case '\\': + case '\?': + dec_ch = str[l + 1]; break; + + case 'a': + dec_ch = '\a'; break; + case 'b': + dec_ch = '\b'; break; + case 'f': + dec_ch = '\f'; break; + case 'n': + dec_ch = '\n'; break; + case 'r': + dec_ch = '\r'; break; + case 't': + dec_ch = '\t'; break; + case 'v': + dec_ch = '\v'; break; + + case '0': + dec_ch = 0; break; + + case 'x': { + if (left - l < 4) { + BLog(BLOG_ERROR, "hexadecimal escape found in string but too little characters follow"); + goto string_fail1; + } + + uintmax_t hex_val; + if (!parse_unsigned_hex_integer_bin(&str[l + 2], 2, &hex_val)) { + BLog(BLOG_ERROR, "hexadecimal escape found in string but two hex characters don't follow"); + goto string_fail1; + } + + dec_ch = hex_val; + extra = 2; + } break; + + default: + BLog(BLOG_ERROR, "bad escape sequence in string"); + goto string_fail1; + } + + l += 2 + extra; + } + else if (str[l] == '"') { + break; + } + else { + dec_ch = str[l]; + l++; + } + + // append character to string + if (!ExpString_AppendByte(&estr, dec_ch)) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto string_fail1; + } + } + + // make sure ending quote was found + if (l == left) { + BLog(BLOG_ERROR, "missing ending quote for string"); + goto string_fail1; + } + + // skip ending quote + l++; + + token = NCD_TOKEN_STRING; + token_val = ExpString_Get(&estr); + token_len = ExpString_Length(&estr); + break; + + string_fail1: + ExpString_Free(&estr); + string_fail0: + error = 1; + } while (0); + else if (is_space_char(*str)) { + token = 0; + l = 1; + } + else { + BLog(BLOG_ERROR, "unrecognized character"); + error = 1; + } + + out: + // report error + if (error) { + output(user, NCD_ERROR, NULL, 0, line, line_char); + return; + } + + // output token + if (token) { + if (!output(user, token, token_val, token_len, line, line_char)) { + return; + } + } + + // update line/char counters + for (size_t i = 0; i < l; i++) { + if (str[i] == '\n') { + line++; + line_char = 1; + } else { + line_char++; + } + } + + str += l; + left -= l; + } + + output(user, NCD_EOF, NULL, 0, line, line_char); +} diff --git a/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.h b/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.h new file mode 100644 index 0000000..38edfad --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDConfigTokenizer.h @@ -0,0 +1,64 @@ +/** + * @file NCDConfigTokenizer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H +#define BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H + +#include + +#define NCD_ERROR -1 +#define NCD_EOF 0 +#define NCD_TOKEN_CURLY_OPEN 1 +#define NCD_TOKEN_CURLY_CLOSE 2 +#define NCD_TOKEN_ROUND_OPEN 3 +#define NCD_TOKEN_ROUND_CLOSE 4 +#define NCD_TOKEN_SEMICOLON 5 +#define NCD_TOKEN_DOT 6 +#define NCD_TOKEN_COMMA 7 +#define NCD_TOKEN_PROCESS 8 +#define NCD_TOKEN_NAME 9 +#define NCD_TOKEN_STRING 10 +#define NCD_TOKEN_ARROW 11 +#define NCD_TOKEN_TEMPLATE 12 +#define NCD_TOKEN_COLON 13 +#define NCD_TOKEN_BRACKET_OPEN 14 +#define NCD_TOKEN_BRACKET_CLOSE 15 +#define NCD_TOKEN_IF 16 +#define NCD_TOKEN_ELIF 17 +#define NCD_TOKEN_ELSE 18 +#define NCD_TOKEN_FOREACH 19 +#define NCD_TOKEN_AS 20 +#define NCD_TOKEN_INCLUDE 21 +#define NCD_TOKEN_INCLUDE_GUARD 22 + +typedef int (*NCDConfigTokenizer_output) (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char); + +void NCDConfigTokenizer_Tokenize (char *str, size_t str_len, NCDConfigTokenizer_output output, void *user); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpProcess.c b/app/src/main/jni/badvpn/ncd/NCDInterpProcess.c new file mode 100644 index 0000000..4462ea6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpProcess.c @@ -0,0 +1,497 @@ +/** + * @file NCDInterpProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "NCDInterpProcess.h" + +#include + +static int compute_prealloc (NCDInterpProcess *o) +{ + int size = 0; + + for (int i = 0; i < o->num_stmts; i++) { + int mod = size % BMAX_ALIGN; + int align_size = (mod == 0 ? 0 : BMAX_ALIGN - mod); + + if (align_size + o->stmts[i].alloc_size > INT_MAX - size) { + return 0; + } + + o->stmts[i].prealloc_offset = size + align_size; + size += align_size + o->stmts[i].alloc_size; + } + + ASSERT(size >= 0) + + o->prealloc_size = size; + + return 1; +} + +static int convert_value_recurser (NCDPlaceholderDb *pdb, NCDStringIndex *string_index, NCDValue *value, NCDValMem *mem, NCDValRef *out) +{ + ASSERT(pdb) + ASSERT(string_index) + ASSERT((NCDValue_Type(value), 1)) + ASSERT(mem) + ASSERT(out) + + switch (NCDValue_Type(value)) { + case NCDVALUE_STRING: { + const char *str = NCDValue_StringValue(value); + size_t len = NCDValue_StringLength(value); + + NCD_string_id_t string_id = NCDStringIndex_GetBin(string_index, str, len); + if (string_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed"); + goto fail; + } + + *out = NCDVal_NewIdString(mem, string_id, string_index); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + } break; + + case NCDVALUE_LIST: { + *out = NCDVal_NewList(mem, NCDValue_ListCount(value)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (NCDValue *e = NCDValue_ListFirst(value); e; e = NCDValue_ListNext(value, e)) { + NCDValRef vval; + if (!convert_value_recurser(pdb, string_index, e, mem, &vval)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, vval)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + } + } break; + + case NCDVALUE_MAP: { + *out = NCDVal_NewMap(mem, NCDValue_MapCount(value)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (NCDValue *ekey = NCDValue_MapFirstKey(value); ekey; ekey = NCDValue_MapNextKey(value, ekey)) { + NCDValue *eval = NCDValue_MapKeyValue(value, ekey); + + NCDValRef vkey; + NCDValRef vval; + if (!convert_value_recurser(pdb, string_index, ekey, mem, &vkey) || + !convert_value_recurser(pdb, string_index, eval, mem, &vval) + ) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(*out, vkey, vval, &inserted)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + if (!inserted) { + BLog(BLOG_ERROR, "duplicate key in map"); + goto fail; + } + } + } break; + + case NCDVALUE_VAR: { + int plid; + if (!NCDPlaceholderDb_AddVariable(pdb, NCDValue_VarName(value), &plid)) { + goto fail; + } + + if (NCDVAL_MINIDX + plid >= -1) { + goto fail; + } + + *out = NCDVal_NewPlaceholder(mem, plid); + } break; + + default: + goto fail; + } + + return 1; + +fail: + return 0; +} + +int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) +{ + ASSERT(process) + ASSERT(string_index) + ASSERT(pdb) + ASSERT(module_index) + + NCDBlock *block = NCDProcess_Block(process); + + if (NCDBlock_NumStatements(block) > INT_MAX) { + BLog(BLOG_ERROR, "too many statements"); + goto fail0; + } + int num_stmts = NCDBlock_NumStatements(block); + + if (!(o->stmts = BAllocArray(num_stmts, sizeof(o->stmts[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + o->num_hash_buckets = num_stmts; + + if (!(o->hash_buckets = BAllocArray(o->num_hash_buckets, sizeof(o->hash_buckets[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + for (size_t i = 0; i < o->num_hash_buckets; i++) { + o->hash_buckets[i] = -1; + } + + if (!(o->name = b_strdup(NCDProcess_Name(process)))) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail2; + } + + o->num_stmts = 0; + o->prealloc_size = -1; + o->is_template = NCDProcess_IsTemplate(process); + o->cache = NULL; + + for (NCDStatement *s = NCDBlock_FirstStatement(block); s; s = NCDBlock_NextStatement(block, s)) { + ASSERT(NCDStatement_Type(s) == NCDSTATEMENT_REG) + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + + e->name = -1; + e->objnames = NULL; + e->num_objnames = 0; + e->alloc_size = 0; + + if (NCDStatement_Name(s)) { + e->name = NCDStringIndex_Get(string_index, NCDStatement_Name(s)); + if (e->name < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + } + + e->cmdname = NCDStringIndex_Get(string_index, NCDStatement_RegCmdName(s)); + if (e->cmdname < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + + NCDValMem_Init(&e->arg_mem); + + NCDValRef val; + if (!convert_value_recurser(pdb, string_index, NCDStatement_RegArgs(s), &e->arg_mem, &val)) { + BLog(BLOG_ERROR, "convert_value_recurser failed"); + goto loop_fail1; + } + + e->arg_ref = NCDVal_ToSafe(val); + + if (!NCDValReplaceProg_Init(&e->arg_prog, val)) { + BLog(BLOG_ERROR, "NCDValReplaceProg_Init failed"); + goto loop_fail1; + } + + if (NCDStatement_RegObjName(s)) { + if (!ncd_make_name_indices(string_index, NCDStatement_RegObjName(s), &e->objnames, &e->num_objnames)) { + BLog(BLOG_ERROR, "ncd_make_name_indices failed"); + goto loop_fail2; + } + + e->binding.method_name_id = NCDModuleIndex_GetMethodNameId(module_index, NCDStatement_RegCmdName(s)); + if (e->binding.method_name_id == -1) { + BLog(BLOG_ERROR, "NCDModuleIndex_GetMethodNameId failed"); + goto loop_fail3; + } + } else { + e->binding.simple_module = NCDModuleIndex_FindModule(module_index, NCDStatement_RegCmdName(s)); + } + + if (e->name >= 0) { + size_t bucket_idx = e->name % o->num_hash_buckets; + e->hash_next = o->hash_buckets[bucket_idx]; + o->hash_buckets[bucket_idx] = o->num_stmts; + } + + o->num_stmts++; + continue; + + loop_fail3: + BFree(e->objnames); + loop_fail2: + NCDValReplaceProg_Free(&e->arg_prog); + loop_fail1: + NCDValMem_Free(&e->arg_mem); + loop_fail0: + goto fail3; + } + + ASSERT(o->num_stmts == num_stmts) + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + while (o->num_stmts-- > 0) { + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + BFree(e->objnames); + NCDValReplaceProg_Free(&e->arg_prog); + NCDValMem_Free(&e->arg_mem); + } + free(o->name); +fail2: + BFree(o->hash_buckets); +fail1: + BFree(o->stmts); +fail0: + return 0; +} + +void NCDInterpProcess_Free (NCDInterpProcess *o) +{ + DebugObject_Free(&o->d_obj); + + while (o->num_stmts-- > 0) { + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + BFree(e->objnames); + NCDValReplaceProg_Free(&e->arg_prog); + NCDValMem_Free(&e->arg_mem); + } + + free(o->name); + BFree(o->hash_buckets); + BFree(o->stmts); +} + +int NCDInterpProcess_FindStatement (NCDInterpProcess *o, int from_index, NCD_string_id_t name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(from_index >= 0) + ASSERT(from_index <= o->num_stmts) + + size_t bucket_idx = name % o->num_hash_buckets; + int stmt_idx = o->hash_buckets[bucket_idx]; + ASSERT(stmt_idx >= -1) + ASSERT(stmt_idx < o->num_stmts) + + while (stmt_idx >= 0) { + if (stmt_idx < from_index && o->stmts[stmt_idx].name == name) { + return stmt_idx; + } + + stmt_idx = o->stmts[stmt_idx].hash_next; + ASSERT(stmt_idx >= -1) + ASSERT(stmt_idx < o->num_stmts) + } + + return -1; +} + +const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i, NCDStringIndex *string_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(string_index) + + return NCDStringIndex_Value(string_index, o->stmts[i].cmdname); +} + +void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(out_objnames) + ASSERT(out_num_objnames) + + *out_objnames = o->stmts[i].objnames; + *out_num_objnames = o->stmts[i].num_objnames; +} + +const struct NCDInterpModule * NCDInterpProcess_StatementGetSimpleModule (NCDInterpProcess *o, int i, NCDStringIndex *string_index, NCDModuleIndex *module_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(!o->stmts[i].objnames) + + struct NCDInterpProcess__stmt *e = &o->stmts[i]; + + if (!e->binding.simple_module) { + const char *cmdname = NCDStringIndex_Value(string_index, e->cmdname); + e->binding.simple_module = NCDModuleIndex_FindModule(module_index, cmdname); + } + + return e->binding.simple_module; +} + +const struct NCDInterpModule * NCDInterpProcess_StatementGetMethodModule (NCDInterpProcess *o, int i, NCD_string_id_t obj_type, NCDModuleIndex *module_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->stmts[i].objnames) + ASSERT(obj_type >= 0) + ASSERT(module_index) + + return NCDModuleIndex_GetMethodModule(module_index, obj_type, o->stmts[i].binding.method_name_id); +} + +int NCDInterpProcess_CopyStatementArgs (NCDInterpProcess *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(out_valmem) + ASSERT(out_val) + ASSERT(out_prog) + + struct NCDInterpProcess__stmt *e = &o->stmts[i]; + + if (!NCDValMem_InitCopy(out_valmem, &e->arg_mem)) { + return 0; + } + + *out_val = NCDVal_FromSafe(out_valmem, e->arg_ref); + *out_prog = e->arg_prog; + return 1; +} + +void NCDInterpProcess_StatementBumpAllocSize (NCDInterpProcess *o, int i, int alloc_size) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(alloc_size >= 0) + + if (alloc_size > o->stmts[i].alloc_size) { + o->stmts[i].alloc_size = alloc_size; + o->prealloc_size = -1; + } +} + +int NCDInterpProcess_PreallocSize (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->prealloc_size == -1 || o->prealloc_size >= 0) + + if (o->prealloc_size < 0 && !compute_prealloc(o)) { + return -1; + } + + return o->prealloc_size; +} + +int NCDInterpProcess_StatementPreallocSize (NCDInterpProcess *o, int i) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->prealloc_size >= 0) + + return o->stmts[i].alloc_size; +} + +int NCDInterpProcess_StatementPreallocOffset (NCDInterpProcess *o, int i) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->prealloc_size >= 0) + + return o->stmts[i].prealloc_offset; +} + +const char * NCDInterpProcess_Name (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->name; +} + +int NCDInterpProcess_IsTemplate (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->is_template; +} + +int NCDInterpProcess_NumStatements (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->num_stmts; +} + +int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem) +{ + DebugObject_Access(&o->d_obj); + ASSERT(elem) + + if (o->cache) { + return 0; + } + + o->cache = elem; + + return 1; +} + +void * NCDInterpProcess_CachePull (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + void *elem = o->cache; + o->cache = NULL; + + return elem; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpProcess.h b/app/src/main/jni/badvpn/ncd/NCDInterpProcess.h new file mode 100644 index 0000000..49fd2c6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpProcess.h @@ -0,0 +1,100 @@ +/** + * @file NCDInterpProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDINTERPPROCESS_H +#define BADVPN_NCDINTERPPROCESS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct NCDInterpProcess__stmt { + NCD_string_id_t name; + NCD_string_id_t cmdname; + NCD_string_id_t *objnames; + size_t num_objnames; + union { + const struct NCDInterpModule *simple_module; + int method_name_id; + } binding; + NCDValMem arg_mem; + NCDValSafeRef arg_ref; + NCDValReplaceProg arg_prog; + int alloc_size; + int prealloc_offset; + int hash_next; +}; + +/** + * A data structure which contains information about a process or + * template, suitable for efficient interpretation. These structures + * are built at startup from the program AST for all processes and + * templates by \link NCDInterpProg. They are not modified after + * the program is loaded Inn case of template processes, the same + * NCDInterpProcess is shared by all processes created from the same + * template. + */ +typedef struct { + struct NCDInterpProcess__stmt *stmts; + char *name; + int num_stmts; + int prealloc_size; + int is_template; + int *hash_buckets; + size_t num_hash_buckets; + void *cache; + DebugObject d_obj; +} NCDInterpProcess; + +int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) WARN_UNUSED; +void NCDInterpProcess_Free (NCDInterpProcess *o); +int NCDInterpProcess_FindStatement (NCDInterpProcess *o, int from_index, NCD_string_id_t name); +const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i, NCDStringIndex *string_index); +void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames); +const struct NCDInterpModule * NCDInterpProcess_StatementGetSimpleModule (NCDInterpProcess *o, int i, NCDStringIndex *string_index, NCDModuleIndex *module_index); +const struct NCDInterpModule * NCDInterpProcess_StatementGetMethodModule (NCDInterpProcess *o, int i, NCD_string_id_t obj_type, NCDModuleIndex *module_index); +int NCDInterpProcess_CopyStatementArgs (NCDInterpProcess *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) WARN_UNUSED; +void NCDInterpProcess_StatementBumpAllocSize (NCDInterpProcess *o, int i, int alloc_size); +int NCDInterpProcess_PreallocSize (NCDInterpProcess *o); +int NCDInterpProcess_StatementPreallocSize (NCDInterpProcess *o, int i); +int NCDInterpProcess_StatementPreallocOffset (NCDInterpProcess *o, int i); +const char * NCDInterpProcess_Name (NCDInterpProcess *o); +int NCDInterpProcess_IsTemplate (NCDInterpProcess *o); +int NCDInterpProcess_NumStatements (NCDInterpProcess *o); +int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem) WARN_UNUSED; +void * NCDInterpProcess_CachePull (NCDInterpProcess *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpProg.c b/app/src/main/jni/badvpn/ncd/NCDInterpProg.c new file mode 100644 index 0000000..7ee75de --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpProg.c @@ -0,0 +1,140 @@ +/** + * @file NCDInterpProg.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "NCDInterpProg.h" + +#include + +#include "NCDInterpProg_hash.h" +#include + +int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) +{ + ASSERT(prog) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE)) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE_GUARD)) + ASSERT(string_index) + ASSERT(pdb) + ASSERT(module_index) + + if (NCDProgram_NumElems(prog) > INT_MAX) { + BLog(BLOG_ERROR, "too many processes"); + goto fail0; + } + int num_procs = NCDProgram_NumElems(prog); + + if (!(o->procs = BAllocArray(num_procs, sizeof(o->procs[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + if (!NCDInterpProg__Hash_Init(&o->hash, num_procs)) { + BLog(BLOG_ERROR, "NCDInterpProg__Hash_Init failed"); + goto fail1; + } + + o->num_procs = 0; + + for (NCDProgramElem *elem = NCDProgram_FirstElem(prog); elem; elem = NCDProgram_NextElem(prog, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *p = NCDProgramElem_Process(elem); + + struct NCDInterpProg__process *e = &o->procs[o->num_procs]; + + e->name = NCDStringIndex_Get(string_index, NCDProcess_Name(p)); + if (e->name < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail2; + } + + if (!NCDInterpProcess_Init(&e->iprocess, p, string_index, pdb, module_index)) { + BLog(BLOG_ERROR, "NCDInterpProcess_Init failed"); + goto fail2; + } + + NCDInterpProg__HashRef ref = {e, o->num_procs}; + if (!NCDInterpProg__Hash_Insert(&o->hash, o->procs, ref, NULL)) { + BLog(BLOG_ERROR, "duplicate process or template name: %s", NCDProcess_Name(p)); + NCDInterpProcess_Free(&e->iprocess); + goto fail2; + } + + o->num_procs++; + } + + ASSERT(o->num_procs == num_procs) + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + while (o->num_procs-- > 0) { + NCDInterpProcess_Free(&o->procs[o->num_procs].iprocess); + } + NCDInterpProg__Hash_Free(&o->hash); +fail1: + BFree(o->procs); +fail0: + return 0; +} + +void NCDInterpProg_Free (NCDInterpProg *o) +{ + DebugObject_Free(&o->d_obj); + + while (o->num_procs-- > 0) { + NCDInterpProcess_Free(&o->procs[o->num_procs].iprocess); + } + + NCDInterpProg__Hash_Free(&o->hash); + + BFree(o->procs); +} + +NCDInterpProcess * NCDInterpProg_FindProcess (NCDInterpProg *o, NCD_string_id_t name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(name >= 0) + + NCDInterpProg__HashRef ref = NCDInterpProg__Hash_Lookup(&o->hash, o->procs, name); + if (ref.link == NCDInterpProg__HashNullLink()) { + return NULL; + } + + ASSERT(ref.ptr->name == name) + + return &ref.ptr->iprocess; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpProg.h b/app/src/main/jni/badvpn/ncd/NCDInterpProg.h new file mode 100644 index 0000000..2c8aaff --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpProg.h @@ -0,0 +1,63 @@ +/** + * @file NCDInterpProg.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDINTERPPROG_H +#define BADVPN_NCDINTERPPROG_H + +#include +#include +#include +#include +#include +#include + +struct NCDInterpProg__process { + NCD_string_id_t name; + NCDInterpProcess iprocess; + int hash_next; +}; + +typedef struct NCDInterpProg__process NCDInterpProg__hashentry; +typedef struct NCDInterpProg__process *NCDInterpProg__hasharg; + +#include "NCDInterpProg_hash.h" +#include + +typedef struct { + struct NCDInterpProg__process *procs; + int num_procs; + NCDInterpProg__Hash hash; + DebugObject d_obj; +} NCDInterpProg; + +int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) WARN_UNUSED; +void NCDInterpProg_Free (NCDInterpProg *o); +NCDInterpProcess * NCDInterpProg_FindProcess (NCDInterpProg *o, NCD_string_id_t name); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpProg_hash.h b/app/src/main/jni/badvpn/ncd/NCDInterpProg_hash.h new file mode 100644 index 0000000..fa8898e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpProg_hash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDInterpProg__Hash +#define CHASH_PARAM_ENTRY NCDInterpProg__hashentry +#define CHASH_PARAM_LINK int +#define CHASH_PARAM_KEY NCD_string_id_t +#define CHASH_PARAM_ARG NCDInterpProg__hasharg +#define CHASH_PARAM_NULL ((int)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) ((size_t)(entry).ptr->name) +#define CHASH_PARAM_KEYHASH(arg, key) ((size_t)(key)) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) ((entry1).ptr->name == (entry2).ptr->name) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) ((key1) == (entry2).ptr->name) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpreter.c b/app/src/main/jni/badvpn/ncd/NCDInterpreter.c new file mode 100644 index 0000000..dc05cc5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpreter.c @@ -0,0 +1,1356 @@ +/** + * @file NCDInterpreter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NCDInterpreter.h" + +#include + +#define SSTATE_CHILD 0 +#define SSTATE_ADULT 1 +#define SSTATE_DYING 2 +#define SSTATE_FORGOTTEN 3 + +#define PSTATE_WORKING 0 +#define PSTATE_UP 1 +#define PSTATE_WAITING 2 +#define PSTATE_TERMINATING 3 + +struct statement { + NCDModuleInst inst; + NCDValMem args_mem; + int mem_size; + int i; +}; + +struct process { + NCDInterpreter *interp; + BReactor *reactor; + NCDInterpProcess *iprocess; + NCDModuleProcess *module_process; + BSmallTimer wait_timer; + BSmallPending work_job; + LinkedList1Node list_node; // node in processes + int ap; + int fp; + int num_statements; + unsigned int error:1; + unsigned int have_alloc:1; +#ifndef NDEBUG + int state; +#endif + struct statement statements[]; +}; + +static void start_terminate (NCDInterpreter *interp, int exit_code); +static char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del); +static void clear_process_cache (NCDInterpreter *interp); +static struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess); +static void process_release (struct process *p, int no_push); +static void process_assert_statements_cleared (struct process *p); +static int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process); +static void process_free (struct process *p, NCDModuleProcess **out_mp); +static void process_set_state (struct process *p, int state); +static void process_start_terminating (struct process *p); +static int process_have_child (struct process *p); +static void process_assert_pointers (struct process *p); +static void process_logfunc (struct process *p); +static void process_log (struct process *p, int level, const char *fmt, ...); +static void process_work_job_handler_working (struct process *p); +static void process_work_job_handler_up (struct process *p); +static void process_work_job_handler_waiting (struct process *p); +static void process_work_job_handler_terminating (struct process *p); +static int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out); +static void process_advance (struct process *p); +static void process_wait_timer_handler (BSmallTimer *timer); +static int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object); +static int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object); +static int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value); +static void statement_logfunc (struct statement *ps); +static void statement_log (struct statement *ps, int level, const char *fmt, ...); +static struct process * statement_process (struct statement *ps); +static int statement_mem_is_allocated (struct statement *ps); +static int statement_mem_size (struct statement *ps); +static int statement_allocate_memory (struct statement *ps, int alloc_size); +static void statement_instance_func_event (NCDModuleInst *inst, int event); +static int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object); +static int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess *mp, NCD_string_id_t template_name); +static void statement_instance_logfunc (NCDModuleInst *inst); +static void statement_instance_func_interp_exit (void *vinterp, int exit_code); +static int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value); +static btime_t statement_instance_func_interp_getretrytime (void *vinterp); +static int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group); +static void process_moduleprocess_func_event (struct process *p, int event); +static int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object); + +#define STATEMENT_LOG(ps, channel, ...) if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, channel)) statement_log(ps, channel, __VA_ARGS__) + +int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterpreter_params params) +{ + ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE)); + ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE_GUARD)); + ASSERT(params.handler_finished); + ASSERT(params.num_extra_args >= 0); + ASSERT(params.reactor); +#ifndef BADVPN_NO_PROCESS + ASSERT(params.manager); +#endif +#ifndef BADVPN_NO_UDEV + ASSERT(params.umanager); +#endif +#ifndef BADVPN_NO_RANDOM + ASSERT(params.random2); +#endif + + // set params + o->params = params; + + // set not terminating + o->terminating = 0; + + // set program + o->program = program; + + // init string index + if (!NCDStringIndex_Init(&o->string_index)) { + BLog(BLOG_ERROR, "NCDStringIndex_Init failed"); + goto fail0; + } + + // init module index + if (!NCDModuleIndex_Init(&o->mindex, &o->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_Init failed"); + goto fail2; + } + + // init pointers to global resources in out struct NCDModuleInst_iparams. + // Don't initialize any callback at this point as these must not be called + // from globalinit functions of modules. + o->module_iparams.reactor = params.reactor; +#ifndef BADVPN_NO_PROCESS + o->module_iparams.manager = params.manager; +#endif +#ifndef BADVPN_NO_UDEV + o->module_iparams.umanager = params.umanager; +#endif +#ifndef BADVPN_NO_RANDOM + o->module_iparams.random2 = params.random2; +#endif + o->module_iparams.string_index = &o->string_index; + + // add module groups to index and allocate string id's for base_type's + for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) { + if (!NCDModuleIndex_AddGroup(&o->mindex, *g, &o->module_iparams, &o->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed"); + goto fail3; + } + } + + // desugar + if (!NCDSugar_Desugar(&o->program)) { + BLog(BLOG_ERROR, "NCDSugar_Desugar failed"); + goto fail3; + } + + // init placeholder database + if (!NCDPlaceholderDb_Init(&o->placeholder_db, &o->string_index)) { + BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed"); + goto fail3; + } + + // init interp program + if (!NCDInterpProg_Init(&o->iprogram, &o->program, &o->string_index, &o->placeholder_db, &o->mindex)) { + BLog(BLOG_ERROR, "NCDInterpProg_Init failed"); + goto fail5; + } + + // init the rest of the module parameters structures + o->module_params.func_event = statement_instance_func_event; + o->module_params.func_getobj = statement_instance_func_getobj; + o->module_params.logfunc = (BLog_logfunc)statement_instance_logfunc; + o->module_params.iparams = &o->module_iparams; + o->module_iparams.user = o; + o->module_iparams.func_initprocess = statement_instance_func_initprocess; + o->module_iparams.func_interp_exit = statement_instance_func_interp_exit; + o->module_iparams.func_interp_getargs = statement_instance_func_interp_getargs; + o->module_iparams.func_interp_getretrytime = statement_instance_func_interp_getretrytime; + o->module_iparams.func_loadgroup = statement_instance_func_interp_loadgroup; + + // init processes list + LinkedList1_Init(&o->processes); + + // init processes + for (NCDProgramElem *elem = NCDProgram_FirstElem(&o->program); elem; elem = NCDProgram_NextElem(&o->program, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *p = NCDProgramElem_Process(elem); + + if (NCDProcess_IsTemplate(p)) { + continue; + } + + // get string id for process name + NCD_string_id_t name_id = NCDStringIndex_Lookup(&o->string_index, NCDProcess_Name(p)); + ASSERT(name_id >= 0) + + // find iprocess + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&o->iprogram, name_id); + ASSERT(iprocess) + + if (!process_new(o, iprocess, NULL)) { + BLog(BLOG_ERROR, "failed to initialize process, exiting"); + goto fail7; + } + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail7:; + // free processes + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->processes)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor)); + NCDModuleProcess *mp; + process_free(p, &mp); + ASSERT(!mp) + } + // clear process cache (process_free() above may push to cache) + clear_process_cache(o); + // free interp program + NCDInterpProg_Free(&o->iprogram); +fail5: + // free placeholder database + NCDPlaceholderDb_Free(&o->placeholder_db); +fail3: + // free module index + NCDModuleIndex_Free(&o->mindex); +fail2: + // free string index + NCDStringIndex_Free(&o->string_index); +fail0: + // free program AST + NCDProgram_Free(&o->program); + return 0; +} + +void NCDInterpreter_Free (NCDInterpreter *o) +{ + DebugObject_Free(&o->d_obj); + // any process that exists must be completely uninitialized + + // free processes + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->processes)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor)); + NCDModuleProcess *mp; + process_free(p, &mp); + ASSERT(!mp) + } + + // clear process cache + clear_process_cache(o); + + // free interp program + NCDInterpProg_Free(&o->iprogram); + + // free placeholder database + NCDPlaceholderDb_Free(&o->placeholder_db); + + // free module index + NCDModuleIndex_Free(&o->mindex); + + // free string index + NCDStringIndex_Free(&o->string_index); + + // free program AST + NCDProgram_Free(&o->program); +} + +void NCDInterpreter_RequestShutdown (NCDInterpreter *o, int exit_code) +{ + DebugObject_Access(&o->d_obj); + + start_terminate(o, exit_code); +} + +void start_terminate (NCDInterpreter *interp, int exit_code) +{ + // remember exit code + interp->main_exit_code = exit_code; + + // if we're already terminating, there's nothing to do + if (interp->terminating) { + return; + } + + // set the terminating flag + interp->terminating = 1; + + // if there are no processes, we're done + if (LinkedList1_IsEmpty(&interp->processes)) { + interp->params.handler_finished(interp->params.user, interp->main_exit_code); + return; + } + + // start terminating non-template processes + for (LinkedList1Node *ln = LinkedList1_GetFirst(&interp->processes); ln; ln = LinkedList1Node_Next(ln)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + if (p->module_process) { + continue; + } + process_start_terminating(p); + } +} + +char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del) +{ + ExpString str; + if (!ExpString_Init(&str)) { + goto fail0; + } + + int is_first = 1; + + while (num_names > 0) { + if (!is_first && !ExpString_AppendChar(&str, del)) { + goto fail1; + } + const char *name_str = NCDStringIndex_Value(&interp->string_index, *names); + if (!ExpString_Append(&str, name_str)) { + goto fail1; + } + names++; + num_names--; + is_first = 0; + } + + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +void clear_process_cache (NCDInterpreter *interp) +{ + for (NCDProgramElem *elem = NCDProgram_FirstElem(&interp->program); elem; elem = NCDProgram_NextElem(&interp->program, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *ast_p = NCDProgramElem_Process(elem); + + NCD_string_id_t name_id = NCDStringIndex_Lookup(&interp->string_index, NCDProcess_Name(ast_p)); + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, name_id); + + struct process *p; + while (p = NCDInterpProcess_CachePull(iprocess)) { + process_release(p, 1); + } + } +} + +struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess) +{ + ASSERT(iprocess) + + // try to pull from cache + struct process *p = NCDInterpProcess_CachePull(iprocess); + if (p) { + goto allocated; + } + + // get number of statements + int num_statements = NCDInterpProcess_NumStatements(iprocess); + + // get size of preallocated memory + int mem_size = NCDInterpProcess_PreallocSize(iprocess); + if (mem_size < 0) { + goto fail0; + } + + // start with size of process structure + size_t alloc_size = sizeof(struct process); + + // add size of statements array + if (num_statements > SIZE_MAX / sizeof(struct statement)) { + goto fail0; + } + if (!BSizeAdd(&alloc_size, num_statements * sizeof(struct statement))) { + goto fail0; + } + + // align for preallocated memory + if (!BSizeAlign(&alloc_size, BMAX_ALIGN)) { + goto fail0; + } + size_t mem_off = alloc_size; + + // add size of preallocated memory + if (mem_size > SIZE_MAX || !BSizeAdd(&alloc_size, mem_size)) { + goto fail0; + } + + // allocate memory + p = BAlloc(alloc_size); + if (!p) { + goto fail0; + } + + // set variables + p->interp = interp; + p->reactor = interp->params.reactor; + p->iprocess = iprocess; + p->ap = 0; + p->fp = 0; + p->num_statements = num_statements; + p->error = 0; + p->have_alloc = 0; + + // init statements + char *mem = (char *)p + mem_off; + for (int i = 0; i < num_statements; i++) { + struct statement *ps = &p->statements[i]; + ps->i = i; + ps->inst.istate = SSTATE_FORGOTTEN; + ps->mem_size = NCDInterpProcess_StatementPreallocSize(iprocess, i); + ps->inst.mem = mem + NCDInterpProcess_StatementPreallocOffset(iprocess, i); + } + + // init timer + BSmallTimer_Init(&p->wait_timer, process_wait_timer_handler); + + // init work job + BSmallPending_Init(&p->work_job, BReactor_PendingGroup(p->reactor), NULL, NULL); + +allocated: + ASSERT(p->interp == interp) + ASSERT(p->reactor == interp->params.reactor) + ASSERT(p->iprocess == iprocess) + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(p->num_statements == NCDInterpProcess_NumStatements(iprocess)) + ASSERT(p->error == 0) + process_assert_statements_cleared(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(!BSmallTimer_IsRunning(&p->wait_timer)) + + return p; + +fail0: + BLog(BLOG_ERROR, "failed to allocate memory for process %s", NCDInterpProcess_Name(iprocess)); + return NULL; +} + +void process_release (struct process *p, int no_push) +{ + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(p->error == 0) + process_assert_statements_cleared(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(!BSmallTimer_IsRunning(&p->wait_timer)) + + // try to push to cache + if (!no_push && !p->have_alloc) { + if (NCDInterpProcess_CachePush(p->iprocess, p)) { + return; + } + } + + // free work job + BSmallPending_Free(&p->work_job, BReactor_PendingGroup(p->reactor)); + + // free statement memory + if (p->have_alloc) { + for (int i = 0; i < p->num_statements; i++) { + struct statement *ps = &p->statements[i]; + if (statement_mem_is_allocated(ps)) { + free(ps->inst.mem); + } + } + } + + // free strucure + BFree(p); +} + +void process_assert_statements_cleared (struct process *p) +{ +#ifndef NDEBUG + for (int i = 0; i < p->num_statements; i++) { + ASSERT(p->statements[i].i == i) + ASSERT(p->statements[i].inst.istate == SSTATE_FORGOTTEN) + } +#endif +} + +int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process) +{ + ASSERT(iprocess) + + // allocate prepared process struct + struct process *p = process_allocate(interp, iprocess); + if (!p) { + return 0; + } + + // set module process pointer + p->module_process = module_process; + + // set module process handlers + if (p->module_process) { + NCDModuleProcess_Interp_SetHandlers(p->module_process, p, + (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event, + (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj); + } + + // set state + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // insert to processes list + LinkedList1_Append(&interp->processes, &p->list_node); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + + return 1; +} + +void process_set_state (struct process *p, int state) +{ +#ifndef NDEBUG + p->state = state; +#endif +} + +void process_free (struct process *p, NCDModuleProcess **out_mp) +{ + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(out_mp) + ASSERT(!BSmallPending_IsSet(&p->work_job)) + + // give module process to caller so it can inform the process creator that the process has terminated + *out_mp = p->module_process; + + // remove from processes list + LinkedList1_Remove(&p->interp->processes, &p->list_node); + + // free timer + BReactor_RemoveSmallTimer(p->reactor, &p->wait_timer); + + // clear error + p->error = 0; + + process_release(p, 0); +} + +void process_start_terminating (struct process *p) +{ + // set state terminating + process_set_state(p, PSTATE_TERMINATING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_terminating, p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); +} + +int process_have_child (struct process *p) +{ + return (p->ap > 0 && p->statements[p->ap - 1].inst.istate == SSTATE_CHILD); +} + +void process_assert_pointers (struct process *p) +{ + ASSERT(p->ap <= p->num_statements) + ASSERT(p->fp >= p->ap) + ASSERT(p->fp <= p->num_statements) + +#ifndef NDEBUG + // check AP + for (int i = 0; i < p->ap; i++) { + if (i == p->ap - 1) { + ASSERT(p->statements[i].inst.istate == SSTATE_ADULT || p->statements[i].inst.istate == SSTATE_CHILD) + } else { + ASSERT(p->statements[i].inst.istate == SSTATE_ADULT) + } + } + + // check FP + int fp = p->num_statements; + while (fp > 0 && p->statements[fp - 1].inst.istate == SSTATE_FORGOTTEN) { + fp--; + } + ASSERT(p->fp == fp) +#endif +} + +void process_logfunc (struct process *p) +{ + BLog_Append("process %s: ", NCDInterpProcess_Name(p->iprocess)); +} + +void process_log (struct process *p, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)process_logfunc, p, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void process_work_job_handler_working (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_WORKING) + + // cleaning up? + if (p->ap < p->fp) { + // order the last living statement to die, if needed + struct statement *ps = &p->statements[p->fp - 1]; + if (ps->inst.istate == SSTATE_DYING) { + return; + } + + STATEMENT_LOG(ps, BLOG_INFO, "killing"); + + // set statement state DYING + ps->inst.istate = SSTATE_DYING; + + // order it to die + NCDModuleInst_Die(&ps->inst); + return; + } + + // clean? + if (process_have_child(p)) { + ASSERT(p->ap > 0) + ASSERT(p->ap <= p->num_statements) + + struct statement *ps = &p->statements[p->ap - 1]; + ASSERT(ps->inst.istate == SSTATE_CHILD) + + STATEMENT_LOG(ps, BLOG_INFO, "clean"); + + // report clean + NCDModuleInst_Clean(&ps->inst); + return; + } + + // finished? + if (p->ap == p->num_statements) { + process_log(p, BLOG_INFO, "victory"); + + // set state up + process_set_state(p, PSTATE_UP); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_up, p); + + // set module process up + if (p->module_process) { + NCDModuleProcess_Interp_Up(p->module_process); + } + return; + } + + // advancing? + struct statement *ps = &p->statements[p->ap]; + ASSERT(ps->inst.istate == SSTATE_FORGOTTEN) + + if (p->error) { + STATEMENT_LOG(ps, BLOG_INFO, "waiting after error"); + + // clear error + p->error = 0; + + // set wait timer + BReactor_SetSmallTimer(p->reactor, &p->wait_timer, BTIMER_SET_RELATIVE, p->interp->params.retry_time); + } else { + // advance + process_advance(p); + } +} + +void process_work_job_handler_up (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_UP) + ASSERT(p->ap < p->num_statements || process_have_child(p)) + + // if we have module process, wait for its permission to continue + if (p->module_process) { + // set state waiting + process_set_state(p, PSTATE_WAITING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_waiting, p); + + // set module process down + NCDModuleProcess_Interp_Down(p->module_process); + return; + } + + // set state working + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // delegate the rest to the working handler + process_work_job_handler_working(p); +} + +void process_work_job_handler_waiting (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_WAITING) + + // do absolutely nothing. Having this no-op handler avoids a branch + // in statement_instance_func_event(). +} + +void process_work_job_handler_terminating (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_TERMINATING) + +again: + if (p->fp == 0) { + NCDInterpreter *interp = p->interp; + + // free process + NCDModuleProcess *mp; + process_free(p, &mp); + + // if program is terminating amd there are no more processes, exit program + if (interp->terminating && LinkedList1_IsEmpty(&interp->processes)) { + ASSERT(!mp) + interp->params.handler_finished(interp->params.user, interp->main_exit_code); + return; + } + + // inform the process creator that the process has terminated + if (mp) { + NCDModuleProcess_Interp_Terminated(mp); + return; + } + + return; + } + + // order the last living statement to die, if needed + struct statement *ps = &p->statements[p->fp - 1]; + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + if (ps->inst.istate == SSTATE_DYING) { + return; + } + + STATEMENT_LOG(ps, BLOG_INFO, "killing"); + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // optimize for statements which can be destroyed immediately + if (NCDModuleInst_TryFree(&ps->inst)) { + STATEMENT_LOG(ps, BLOG_INFO, "died"); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set statement state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + + goto again; + } + + // set statement state DYING + ps->inst.istate = SSTATE_DYING; + + // order it to die + NCDModuleInst_Die(&ps->inst); + return; +} + +int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out) +{ + struct process *p = arg; + ASSERT(plid >= 0) + ASSERT(mem) + ASSERT(out) + + const NCD_string_id_t *varnames; + size_t num_names; + NCDPlaceholderDb_GetVariable(&p->interp->placeholder_db, plid, &varnames, &num_names); + + return process_resolve_variable_expr(p, p->ap, varnames, num_names, mem, out); +} + +void process_advance (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->ap == p->fp) + ASSERT(!process_have_child(p)) + ASSERT(p->ap < p->num_statements) + ASSERT(!p->error) + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(p->state == PSTATE_WORKING) + + struct statement *ps = &p->statements[p->ap]; + ASSERT(ps->inst.istate == SSTATE_FORGOTTEN) + + STATEMENT_LOG(ps, BLOG_INFO, "initializing"); + + // need to determine the module and object to use it on (if it's a method) + const struct NCDInterpModule *module; + void *method_context = NULL; + + // get object names, e.g. "my.cat" in "my.cat->meow();" + // (or NULL if this is not a method statement) + const NCD_string_id_t *objnames; + size_t num_objnames; + NCDInterpProcess_StatementObjNames(p->iprocess, p->ap, &objnames, &num_objnames); + + if (!objnames) { + // not a method; module is already known by NCDInterpProcess + module = NCDInterpProcess_StatementGetSimpleModule(p->iprocess, p->ap, &p->interp->string_index, &p->interp->mindex); + + if (!module) { + const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index); + STATEMENT_LOG(ps, BLOG_ERROR, "unknown simple statement: %s", cmdname_str); + goto fail0; + } + } else { + // get object + NCDObject object; + if (!process_resolve_object_expr(p, p->ap, objnames, num_objnames, &object)) { + goto fail0; + } + + // get object type + NCD_string_id_t object_type = NCDObject_Type(&object); + if (object_type < 0) { + STATEMENT_LOG(ps, BLOG_ERROR, "cannot call method on object with no type"); + goto fail0; + } + + // get method context + method_context = NCDObject_MethodUser(&object); + + // find module based on type of object + module = NCDInterpProcess_StatementGetMethodModule(p->iprocess, p->ap, object_type, &p->interp->mindex); + + if (!module) { + const char *type_str = NCDStringIndex_Value(&p->interp->string_index, object_type); + const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index); + STATEMENT_LOG(ps, BLOG_ERROR, "unknown method statement: %s::%s", type_str, cmdname_str); + goto fail0; + } + } + + // copy arguments + NCDValRef args; + NCDValReplaceProg prog; + if (!NCDInterpProcess_CopyStatementArgs(p->iprocess, ps->i, &ps->args_mem, &args, &prog)) { + STATEMENT_LOG(ps, BLOG_ERROR, "NCDInterpProcess_CopyStatementArgs failed"); + goto fail0; + } + + // replace placeholders with values of variables + if (!NCDValReplaceProg_Execute(prog, &ps->args_mem, replace_placeholders_callback, p)) { + STATEMENT_LOG(ps, BLOG_ERROR, "failed to replace variables in arguments with values"); + goto fail1; + } + + // convert non-continuous strings unless the module can handle them + if (!(module->module.flags & NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS)) { + if (!NCDValMem_ConvertNonContinuousStrings(&ps->args_mem, &args)) { + STATEMENT_LOG(ps, BLOG_ERROR, "NCDValMem_ConvertNonContinuousStrings failed"); + goto fail1; + } + } + + // allocate memory + if (!statement_allocate_memory(ps, module->module.alloc_size)) { + STATEMENT_LOG(ps, BLOG_ERROR, "failed to allocate memory"); + goto fail1; + } + + // set statement state CHILD + ps->inst.istate = SSTATE_CHILD; + + // increment AP + p->ap++; + + // increment FP + p->fp++; + + process_assert_pointers(p); + + // initialize module instance + NCDModuleInst_Init(&ps->inst, module, method_context, args, &p->interp->module_params); + return; + +fail1: + NCDValMem_Free(&ps->args_mem); +fail0: + // set error + p->error = 1; + + // schedule work to start the timer + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); +} + +void process_wait_timer_handler (BSmallTimer *timer) +{ + struct process *p = UPPER_OBJECT(timer, struct process, wait_timer); + process_assert_pointers(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + + // check if something happened that means we no longer need to retry + if (p->ap != p->fp || process_have_child(p) || p->ap == p->num_statements) { + return; + } + + process_log(p, BLOG_INFO, "retrying"); + + // advance. Note: the asserts for this are indeed satisfied, though this + // it not trivial to prove. + process_advance(p); +} + +int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(out_object) + + int i = NCDInterpProcess_FindStatement(p->iprocess, pos, name); + if (i >= 0) { + struct statement *ps = &p->statements[i]; + ASSERT(i < p->num_statements) + + if (ps->inst.istate == SSTATE_FORGOTTEN) { + process_log(p, BLOG_ERROR, "statement (%d) is uninitialized", i); + return 0; + } + + *out_object = NCDModuleInst_Object(&ps->inst); + return 1; + } + + if (p->module_process && NCDModuleProcess_Interp_GetSpecialObj(p->module_process, name, out_object)) { + return 1; + } + + return 0; +} + +int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(names) + ASSERT(num_names > 0) + ASSERT(out_object) + + NCDObject object; + if (!process_find_object(p, pos, names[0], &object)) { + goto fail; + } + + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, num_names - 1, out_object)) { + goto fail; + } + + return 1; + +fail:; + char *name = implode_id_strings(p->interp, names, num_names, '.'); + process_log(p, BLOG_ERROR, "failed to resolve object (%s) from position %zu", (name ? name : ""), pos); + free(name); + return 0; +} + +int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(names) + ASSERT(num_names > 0) + ASSERT(mem) + ASSERT(out_value) + + NCDObject object; + if (!process_find_object(p, pos, names[0], &object)) { + goto fail; + } + + if (!NCDObject_ResolveVarExprCompact(&object, names + 1, num_names - 1, mem, out_value)) { + goto fail; + } + + return 1; + +fail:; + char *name = implode_id_strings(p->interp, names, num_names, '.'); + process_log(p, BLOG_ERROR, "failed to resolve variable (%s) from position %zu", (name ? name : ""), pos); + free(name); + return 0; +} + +void statement_logfunc (struct statement *ps) +{ + process_logfunc(statement_process(ps)); + BLog_Append("statement %zu: ", ps->i); +} + +void statement_log (struct statement *ps, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)statement_logfunc, ps, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +struct process * statement_process (struct statement *ps) +{ + return UPPER_OBJECT(ps - ps->i, struct process, statements); +} + +int statement_mem_is_allocated (struct statement *ps) +{ + return (ps->mem_size < 0); +} + +int statement_mem_size (struct statement *ps) +{ + return (ps->mem_size >= 0 ? ps->mem_size : -ps->mem_size); +} + +int statement_allocate_memory (struct statement *ps, int alloc_size) +{ + ASSERT(alloc_size >= 0) + + if (alloc_size > statement_mem_size(ps)) { + // allocate new memory + char *new_mem = malloc(alloc_size); + if (!new_mem) { + STATEMENT_LOG(ps, BLOG_ERROR, "malloc failed"); + return 0; + } + + // release old memory unless it was preallocated + if (statement_mem_is_allocated(ps)) { + free(ps->inst.mem); + } + + struct process *p = statement_process(ps); + + // register memory in statement + ps->inst.mem = new_mem; + ps->mem_size = -alloc_size; + + // set the alloc flag in the process to make sure process_free() + // releases the allocated memory + p->have_alloc = 1; + + // register alloc size for future preallocations + NCDInterpProcess_StatementBumpAllocSize(p->iprocess, ps->i, alloc_size); + } + + return 1; +} + +void statement_instance_func_event (NCDModuleInst *inst, int event) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate == SSTATE_CHILD || ps->inst.istate == SSTATE_ADULT || ps->inst.istate == SSTATE_DYING) + + struct process *p = statement_process(ps); + process_assert_pointers(p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + + switch (event) { + case NCDMODULE_EVENT_UP: { + ASSERT(ps->inst.istate == SSTATE_CHILD) + + STATEMENT_LOG(ps, BLOG_INFO, "up"); + + // set state ADULT + ps->inst.istate = SSTATE_ADULT; + } break; + + case NCDMODULE_EVENT_DOWN: { + ASSERT(ps->inst.istate == SSTATE_ADULT) + + STATEMENT_LOG(ps, BLOG_INFO, "down"); + + // set state CHILD + ps->inst.istate = SSTATE_CHILD; + + // clear error + if (ps->i < p->ap) { + p->error = 0; + } + + // update AP + if (p->ap > ps->i + 1) { + p->ap = ps->i + 1; + } + } break; + + case NCDMODULE_EVENT_DOWNUP: { + ASSERT(ps->inst.istate == SSTATE_ADULT) + + STATEMENT_LOG(ps, BLOG_INFO, "down"); + STATEMENT_LOG(ps, BLOG_INFO, "up"); + + // clear error + if (ps->i < p->ap) { + p->error = 0; + } + + // update AP + if (p->ap > ps->i + 1) { + p->ap = ps->i + 1; + } + } break; + + case NCDMODULE_EVENT_DEAD: { + STATEMENT_LOG(ps, BLOG_INFO, "died"); + + // free instance + NCDModuleInst_Free(&ps->inst); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + } break; + + case NCDMODULE_EVENT_DEADERROR: { + STATEMENT_LOG(ps, BLOG_ERROR, "died with error"); + + // free instance + NCDModuleInst_Free(&ps->inst); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // set error + if (ps->i < p->ap) { + p->error = 1; + } + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + } break; + } +} + +int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + + return process_find_object(statement_process(ps), ps->i, objname, out_object); +} + +int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess* mp, NCD_string_id_t template_name) +{ + NCDInterpreter *interp = vinterp; + + // find process + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, template_name); + if (!iprocess) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "no template named %s", str); + return 0; + } + + // make sure it's a template + if (!NCDInterpProcess_IsTemplate(iprocess)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "need template to create a process, but %s is a process", str); + return 0; + } + + // create process + if (!process_new(interp, iprocess, mp)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "failed to create process from template %s", str); + return 0; + } + + if (BLog_WouldLog(BLOG_INFO, BLOG_CURRENT_CHANNEL)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_INFO, "created process from template %s", str); + } + + return 1; +} + +void statement_instance_logfunc (NCDModuleInst *inst) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + + statement_logfunc(ps); + BLog_Append("module: "); +} + +void statement_instance_func_interp_exit (void *vinterp, int exit_code) +{ + NCDInterpreter *interp = vinterp; + + start_terminate(interp, exit_code); +} + +int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value) +{ + NCDInterpreter *interp = vinterp; + + *out_value = NCDVal_NewList(mem, interp->params.num_extra_args); + if (NCDVal_IsInvalid(*out_value)) { + BLog(BLOG_ERROR, "NCDVal_NewList failed"); + goto fail; + } + + for (int i = 0; i < interp->params.num_extra_args; i++) { + NCDValRef arg = NCDVal_NewString(mem, interp->params.extra_args[i]); + if (NCDVal_IsInvalid(arg)) { + BLog(BLOG_ERROR, "NCDVal_NewString failed"); + goto fail; + } + + if (!NCDVal_ListAppend(*out_value, arg)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + } + + return 1; + +fail: + *out_value = NCDVal_NewInvalid(); + return 1; +} + +btime_t statement_instance_func_interp_getretrytime (void *vinterp) +{ + NCDInterpreter *interp = vinterp; + + return interp->params.retry_time; +} + +int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group) +{ + NCDInterpreter *interp = vinterp; + + if (!NCDModuleIndex_AddGroup(&interp->mindex, group, &interp->module_iparams, &interp->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed"); + return 0; + } + + return 1; +} + +void process_moduleprocess_func_event (struct process *p, int event) +{ + ASSERT(p->module_process) + + switch (event) { + case NCDMODULEPROCESS_INTERP_EVENT_CONTINUE: { + ASSERT(p->state == PSTATE_WAITING) + + // set state working + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + } break; + + case NCDMODULEPROCESS_INTERP_EVENT_TERMINATE: { + ASSERT(p->state != PSTATE_TERMINATING) + + process_log(p, BLOG_INFO, "process termination requested"); + + // start terminating + process_start_terminating(p); + } break; + + default: ASSERT(0); + } +} + +int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(p->module_process) + + return process_find_object(p, p->num_statements, name, out_object); +} diff --git a/app/src/main/jni/badvpn/ncd/NCDInterpreter.h b/app/src/main/jni/badvpn/ncd/NCDInterpreter.h new file mode 100644 index 0000000..8d11c33 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDInterpreter.h @@ -0,0 +1,156 @@ +/** + * @file NCDInterpreter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_INTERPRETER_H +#define BADVPN_NCD_INTERPRETER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_NO_PROCESS +#include +#endif +#ifndef BADVPN_NO_UDEV +#include +#endif +#ifndef BADVPN_NO_RANDOM +#include +#endif + +/** + * Handler called when the interpreter has terminated, and {@link NCDInterpreter_Free} + * can be called. + * + * @param user the user member of struct {@link NCDInterpreter_params} + * @param exit_code the exit code specified in the last interpreter termination request + */ +typedef void (*NCDInterpreter_handler_finished) (void *user, int exit_code); + +struct NCDInterpreter_params { + // callbacks + NCDInterpreter_handler_finished handler_finished; + void *user; + + // options + btime_t retry_time; + char **extra_args; + int num_extra_args; + + // possibly shared resources + BReactor *reactor; +#ifndef BADVPN_NO_PROCESS + BProcessManager *manager; +#endif +#ifndef BADVPN_NO_UDEV + NCDUdevManager *umanager; +#endif +#ifndef BADVPN_NO_RANDOM + BRandom2 *random2; +#endif +}; + +typedef struct { + // parameters + struct NCDInterpreter_params params; + + // are we terminating + int terminating; + int main_exit_code; + + // string index + NCDStringIndex string_index; + + // module index + NCDModuleIndex mindex; + + // program AST + NCDProgram program; + + // placeholder database + NCDPlaceholderDb placeholder_db; + + // structure for efficient interpretation + NCDInterpProg iprogram; + + // common module parameters + struct NCDModuleInst_params module_params; + struct NCDModuleInst_iparams module_iparams; + + // processes + LinkedList1 processes; + + DebugObject d_obj; +} NCDInterpreter; + +/** + * Initializes and starts the interpreter. + * + * @param o the interpreter + * @param program the program to execute in AST format. The program must + * not contain any 'include' or 'include_guard' directives. + * The interpreter takes ownership of the program, regardless + * of the success of this function. + * @param params other parameters + * @return 1 on success, 0 on failure + */ +int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterpreter_params params) WARN_UNUSED; + +/** + * Frees the interpreter. + * This may only be called after the interpreter has terminated, i.e. + * the {@link NCDInterpreter_handler_finished} handler has been called. + * Additionally, it can be called right after {@link NCDInterpreter_Init} + * before any of the interpreter's {@link BPendingGroup} jobs have executed. + */ +void NCDInterpreter_Free (NCDInterpreter *o); + +/** + * Requests termination of the interpreter. + * NOTE: the program can request its own termination, possibly overriding the exit + * code specified here. Expect the program to terminate even if this function was + * not called. + * + * @param o the interpreter + * @param exit_code the exit code to be passed to {@link NCDInterpreter_handler_finished}. + * This overrides any exit code set previously. + */ +void NCDInterpreter_RequestShutdown (NCDInterpreter *o, int exit_code); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDMethodIndex.c b/app/src/main/jni/badvpn/ncd/NCDMethodIndex.c new file mode 100644 index 0000000..5b3662a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDMethodIndex.c @@ -0,0 +1,272 @@ +/** + * @file NCDMethodIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDMethodIndex.h" + +#include "NCDMethodIndex_hash.h" +#include + +#define GROWARRAY_NAME NamesArray +#define GROWARRAY_OBJECT_TYPE NCDMethodIndex +#define GROWARRAY_ARRAY_MEMBER names +#define GROWARRAY_CAPACITY_MEMBER names_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +#define GROWARRAY_NAME EntriesArray +#define GROWARRAY_OBJECT_TYPE NCDMethodIndex +#define GROWARRAY_ARRAY_MEMBER entries +#define GROWARRAY_CAPACITY_MEMBER entries_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +#include + +static int find_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx) +{ + ASSERT(method_name) + + NCDMethodIndex__HashRef ref = NCDMethodIndex__Hash_Lookup(&o->hash, o->names, method_name); + if (ref.link == -1) { + return 0; + } + + ASSERT(ref.link >= 0) + ASSERT(ref.link < o->num_names) + + struct NCDMethodIndex__method_name *name_entry = ref.ptr; + ASSERT(!strcmp(name_entry->method_name, method_name)) + ASSERT(name_entry->first_entry >= 0) + ASSERT(name_entry->first_entry < o->num_entries) + + if (out_entry_idx) { + *out_entry_idx = name_entry->first_entry; + } + return 1; +} + +static int add_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx) +{ + ASSERT(method_name) + ASSERT(!find_method_name(o, method_name, NULL)) + + if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed"); + goto fail0; + } + + if (o->num_names == o->names_capacity && !NamesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "NamesArray_DoubleUp failed"); + goto fail0; + } + + struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries]; + entry->obj_type = -1; + entry->next = -1; + + struct NCDMethodIndex__method_name *name_entry = &o->names[o->num_names]; + + if (!(name_entry->method_name = b_strdup(method_name))) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + + name_entry->first_entry = o->num_entries; + + NCDMethodIndex__HashRef ref = {name_entry, o->num_names}; + int res = NCDMethodIndex__Hash_Insert(&o->hash, o->names, ref, NULL); + ASSERT_EXECUTE(res) + + o->num_entries++; + o->num_names++; + + if (out_entry_idx) { + *out_entry_idx = name_entry->first_entry; + } + return 1; + +fail0: + return 0; +} + +int NCDMethodIndex_Init (NCDMethodIndex *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + o->string_index = string_index; + + if (!NamesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) { + BLog(BLOG_ERROR, "NamesArray_Init failed"); + goto fail0; + } + + if (!EntriesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_ENTRIES)) { + BLog(BLOG_ERROR, "EntriesArray_Init failed"); + goto fail1; + } + + o->num_names = 0; + o->num_entries = 0; + + if (!NCDMethodIndex__Hash_Init(&o->hash, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) { + BLog(BLOG_ERROR, "NCDMethodIndex__Hash_Init failed"); + goto fail2; + } + + return 1; + +fail2: + EntriesArray_Free(o); +fail1: + NamesArray_Free(o); +fail0: + return 0; +} + +void NCDMethodIndex_Free (NCDMethodIndex *o) +{ + for (int i = 0; i < o->num_names; i++) { + free(o->names[i].method_name); + } + + NCDMethodIndex__Hash_Free(&o->hash); + EntriesArray_Free(o); + NamesArray_Free(o); +} + +int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, size_t obj_type_len, const char *method_name, const struct NCDInterpModule *module) +{ + ASSERT(obj_type) + ASSERT(method_name) + ASSERT(module) + + NCD_string_id_t obj_type_id = NCDStringIndex_GetBin(o->string_index, obj_type, obj_type_len); + if (obj_type_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail0; + } + + int entry_idx; + int first_entry_idx; + + if (!find_method_name(o, method_name, &first_entry_idx)) { + if (!add_method_name(o, method_name, &entry_idx)) { + goto fail0; + } + + ASSERT(entry_idx >= 0) + ASSERT(entry_idx < o->num_entries) + + struct NCDMethodIndex__entry *entry = &o->entries[entry_idx]; + + entry->obj_type = obj_type_id; + entry->module = module; + } else { + ASSERT(first_entry_idx >= 0) + ASSERT(first_entry_idx < o->num_entries) + + if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed"); + goto fail0; + } + + entry_idx = o->num_entries; + struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries]; + + entry->obj_type = obj_type_id; + entry->module = module; + + entry->next = o->entries[first_entry_idx].next; + o->entries[first_entry_idx].next = o->num_entries; + + o->num_entries++; + } + + return entry_idx; + +fail0: + return -1; +} + +void NCDMethodIndex_RemoveMethod (NCDMethodIndex *o, int method_name_id) +{ + ASSERT(method_name_id >= 0) + ASSERT(method_name_id < o->num_entries) + ASSERT(o->entries[method_name_id].obj_type >= 0) + + o->entries[method_name_id].obj_type = -1; +} + +int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name) +{ + ASSERT(method_name) + + int first_entry_idx; + + if (!find_method_name(o, method_name, &first_entry_idx)) { + if (!add_method_name(o, method_name, &first_entry_idx)) { + return -1; + } + } + + ASSERT(first_entry_idx >= 0) + ASSERT(first_entry_idx < o->num_entries) + + return first_entry_idx; +} + +const struct NCDInterpModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, NCD_string_id_t obj_type, int method_name_id) +{ + ASSERT(obj_type >= 0) + ASSERT(method_name_id >= 0) + ASSERT(method_name_id < o->num_entries) + + do { + struct NCDMethodIndex__entry *entry = &o->entries[method_name_id]; + + if (entry->obj_type == obj_type) { + ASSERT(entry->module) + return entry->module; + } + + method_name_id = entry->next; + ASSERT(method_name_id >= -1) + ASSERT(method_name_id < o->num_entries) + } while (method_name_id >= 0); + + return NULL; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDMethodIndex.h b/app/src/main/jni/badvpn/ncd/NCDMethodIndex.h new file mode 100644 index 0000000..e4db0a5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDMethodIndex.h @@ -0,0 +1,135 @@ +/** + * @file NCDMethodIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDMETHODINDEX_H +#define BADVPN_NCDMETHODINDEX_H + +#include +#include +#include +#include + +#define NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES 64 +#define NCDMETHODINDEX_NUM_EXPECTED_ENTRIES 64 + +struct NCDMethodIndex__method_name { + char *method_name; + int first_entry; + int hash_next; +}; + +struct NCDMethodIndex__entry { + NCD_string_id_t obj_type; + const struct NCDInterpModule *module; + int next; +}; + +typedef struct NCDMethodIndex__method_name NCDMethodIndex__hashentry; +typedef const char *NCDMethodIndex__hashkey; +typedef struct NCDMethodIndex__method_name *NCDMethodIndex__hasharg; + +#include "NCDMethodIndex_hash.h" +#include + +/** + * The method index associates (object_type, method_name) pairs to pointers + * to corresponding \link NCDInterpModule structures (whose type strings would + * be "object_type::method_name"). + * More precisely, the method names are represented as indices into an + * internal array, which allows very efficient lookup when the method names + * are known in advance, but not the object types. + */ +typedef struct { + struct NCDMethodIndex__method_name *names; + struct NCDMethodIndex__entry *entries; + int names_capacity; + int entries_capacity; + int num_names; + int num_entries; + NCDMethodIndex__Hash hash; + NCDStringIndex *string_index; +} NCDMethodIndex; + +/** + * Initializes the method index. + * + * @return 1 on success, 0 on failure + */ +int NCDMethodIndex_Init (NCDMethodIndex *o, NCDStringIndex *string_index) WARN_UNUSED; + +/** + * Frees the method index. + */ +void NCDMethodIndex_Free (NCDMethodIndex *o); + +/** + * Adds a method to the index. + * Duplicate methods will not be detected here. + * + * @param obj_type object type of method, e.g. "cat" in "cat::meow". + * Must not be NULL. Does not have to be null-terminated. + * @param obj_type_len number of characters in obj_type + * @param method_name name of method, e.g. "meow" in "cat::meow". + * Must not be NULL. + * @param module pointer to module structure. Must not be NULL. + * @return on success, a non-negative identifier; on failure, -1 + */ +int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, size_t obj_type_len, const char *method_name, const struct NCDInterpModule *module); + +/** + * Removes a method from the index. + * + * @param method_name_id method name identifier + */ +void NCDMethodIndex_RemoveMethod (NCDMethodIndex *o, int method_name_id); + +/** + * Obtains an internal integer identifier for a method name. The intention is that + * this is stored and later passed to \link NCDMethodIndex_GetMethodModule for + * efficient lookup of modules corresponding to methods. + * + * @param method_name name of method, e.g. "meow" in "cat::meow". + * Must not be NULL. + * @return non-negative integer on success, -1 on failure + */ +int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name); + +/** + * Looks up the module corresponding to a method. The method name is passed as an + * identifier obtained from \link NCDMethodIndex_GetMethodNameId. + * + * @param obj_type object type of method, e.g. "cat" in "cat::meow", as a string + * identifier via {@link NCDStringIndex} + * @param method_name_id method name identifier. Must have been previously returned + * by a successfull call of \link NCDMethodIndex_GetMethodNameId. + * @return module pointer, or NULL if no such method exists + */ +const struct NCDInterpModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, NCD_string_id_t obj_type, int method_name_id); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDMethodIndex_hash.h b/app/src/main/jni/badvpn/ncd/NCDMethodIndex_hash.h new file mode 100644 index 0000000..f1108cb --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDMethodIndex_hash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDMethodIndex__Hash +#define CHASH_PARAM_ENTRY NCDMethodIndex__hashentry +#define CHASH_PARAM_LINK int +#define CHASH_PARAM_KEY NCDMethodIndex__hashkey +#define CHASH_PARAM_ARG NCDMethodIndex__hasharg +#define CHASH_PARAM_NULL ((int)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) (badvpn_djb2_hash((const uint8_t *)(entry).ptr->method_name)) +#define CHASH_PARAM_KEYHASH(arg, key) (badvpn_djb2_hash((const uint8_t *)(key))) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->method_name, (entry2).ptr->method_name)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->method_name)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/app/src/main/jni/badvpn/ncd/NCDModule.c b/app/src/main/jni/badvpn/ncd/NCDModule.c new file mode 100644 index 0000000..da6894a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDModule.c @@ -0,0 +1,625 @@ +/** + * @file NCDModule.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define STATE_DEAD 0 +#define STATE_DOWN_CLEAN 1 +#define STATE_UP 2 +#define STATE_DOWN_UNCLEAN 3 +#define STATE_DYING 4 + +#define PROCESS_STATE_INIT 0 +#define PROCESS_STATE_DOWN 1 +#define PROCESS_STATE_UP 2 +#define PROCESS_STATE_DOWN_WAITING 3 +#define PROCESS_STATE_TERMINATING 4 +#define PROCESS_STATE_TERMINATED 5 + +static int object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static int object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int process_args_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static int process_arg_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +static void frontend_event (NCDModuleInst *n, int event) +{ + n->params->func_event(n, event); +} + +static void inst_assert_backend (NCDModuleInst *n) +{ + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) +} + +static void set_process_state (NCDModuleProcess *p, int state) +{ +#ifndef NDEBUG + p->state = state; +#endif +} + +void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void *method_context, NCDValRef args, const struct NCDModuleInst_params *params) +{ + ASSERT(m) + ASSERT(m->module.func_new2) + ASSERT(m->module.alloc_size >= 0) + ASSERT(m->base_type_id >= 0) + ASSERT(m->group) + ASSERT(n->mem) + ASSERT(NCDVal_IsList(args)) + ASSERT(params) + ASSERT(params->func_event) + ASSERT(params->func_getobj) + ASSERT(params->logfunc) + ASSERT(params->iparams) + ASSERT(params->iparams->func_initprocess) + ASSERT(params->iparams->func_interp_exit) + ASSERT(params->iparams->func_interp_getargs) + ASSERT(params->iparams->func_interp_getretrytime) + + // init arguments + n->m = m; + n->params = params; + + // set initial state + n->state = STATE_DOWN_CLEAN; + + // give NCDModuleInst to methods, not mem + n->pass_mem_to_methods = 0; + + DebugObject_Init(&n->d_obj); + + struct NCDModuleInst_new_params new_params; + new_params.method_user = method_context; + new_params.args = args; + + n->m->module.func_new2(n->mem, n, &new_params); +} + +void NCDModuleInst_Free (NCDModuleInst *n) +{ + DebugObject_Free(&n->d_obj); + ASSERT(n->state == STATE_DEAD) +} + +void NCDModuleInst_Die (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP || n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + n->state = STATE_DYING; + + if (!n->m->module.func_die) { + NCDModuleInst_Backend_Dead(n); + return; + } + + n->m->module.func_die(n->mem); + return; +} + +int NCDModuleInst_TryFree (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP || n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + if (n->m->module.func_die) { + return 0; + } + + DebugObject_Free(&n->d_obj); + + return 1; +} + +void NCDModuleInst_Clean (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + if (n->state == STATE_DOWN_UNCLEAN) { + n->state = STATE_DOWN_CLEAN; + + if (n->m->module.func_clean) { + n->m->module.func_clean(n->mem); + return; + } + } +} + +NCDObject NCDModuleInst_Object (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->m->base_type_id >= 0) + + void *method_user = (n->pass_mem_to_methods ? n->mem : n); + + return NCDObject_BuildFull(n->m->base_type_id, n, 0, method_user, object_func_getvar, object_func_getobj); +} + +void NCDModuleInst_Backend_PassMemToMethods (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + + n->pass_mem_to_methods = 1; +} + +static int can_resolve (NCDModuleInst *n) +{ + switch (n->state) { + case STATE_UP: + return 1; + case STATE_DOWN_CLEAN: + case STATE_DOWN_UNCLEAN: + return !!(n->m->module.flags & NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN); + default: + return 0; + } +} + +static int object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleInst *n = NCDObject_DataPtr(obj); + DebugObject_Access(&n->d_obj); + + if ((!n->m->module.func_getvar && !n->m->module.func_getvar2) || !can_resolve(n)) { + return 0; + } + + int res; + if (n->m->module.func_getvar2) { + res = n->m->module.func_getvar2(n->mem, name, mem, out_value); + } else { + if (NCDStringIndex_HasNulls(n->params->iparams->string_index, name)) { + return 0; + } + const char *name_str = NCDStringIndex_Value(n->params->iparams->string_index, name); + res = n->m->module.func_getvar(n->mem, name_str, mem, out_value); + } + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +static int object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + NCDModuleInst *n = NCDObject_DataPtr(obj); + DebugObject_Access(&n->d_obj); + + if (!n->m->module.func_getobj || !can_resolve(n)) { + return 0; + } + + int res = n->m->module.func_getobj(n->mem, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +void * NCDModuleInst_Backend_GetUser (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + + return n->mem; +} + +void NCDModuleInst_Backend_Up (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + n->state = STATE_UP; + frontend_event(n, NCDMODULE_EVENT_UP); +} + +void NCDModuleInst_Backend_Down (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP) + + n->state = STATE_DOWN_UNCLEAN; + frontend_event(n, NCDMODULE_EVENT_DOWN); +} + +void NCDModuleInst_Backend_DownUp (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP) + + frontend_event(n, NCDMODULE_EVENT_DOWNUP); +} + +void NCDModuleInst_Backend_Dead (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN || + n->state == STATE_UP || n->state == STATE_DYING) + + n->state = STATE_DEAD; + + frontend_event(n, NCDMODULE_EVENT_DEAD); + return; +} + +void NCDModuleInst_Backend_DeadError (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN || + n->state == STATE_UP || n->state == STATE_DYING) + + n->state = STATE_DEAD; + + frontend_event(n, NCDMODULE_EVENT_DEADERROR); + return; +} + +int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(out_object) + + int res = n->params->func_getobj(n, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...) +{ + DebugObject_Access(&n->d_obj); + + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg(n->params->logfunc, n, channel, level, fmt, vl); + va_end(vl); +} + +void NCDModuleInst_Backend_LogVarArg (NCDModuleInst *n, int channel, int level, const char *fmt, va_list vl) +{ + DebugObject_Access(&n->d_obj); + + BLog_LogViaFuncVarArg(n->params->logfunc, n, channel, level, fmt, vl); +} + +BLogContext NCDModuleInst_Backend_LogContext (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + + return BLog_MakeContext(n->params->logfunc, n); +} + +void NCDModuleInst_Backend_InterpExit (NCDModuleInst *n, int exit_code) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + + n->params->iparams->func_interp_exit(n->params->iparams->user, exit_code); +} + +int NCDModuleInst_Backend_InterpGetArgs (NCDModuleInst *n, NCDValMem *mem, NCDValRef *out_value) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + ASSERT(mem) + ASSERT(out_value) + + int res = n->params->iparams->func_interp_getargs(n->params->iparams->user, mem, out_value); + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +btime_t NCDModuleInst_Backend_InterpGetRetryTime (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + + return n->params->iparams->func_interp_getretrytime(n->params->iparams->user); +} + +int NCDModuleInst_Backend_InterpLoadGroup (NCDModuleInst *n, const struct NCDModuleGroup *group) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + ASSERT(group) + + return n->params->iparams->func_loadgroup(n->params->iparams->user, group); +} + +int NCDModuleProcess_InitId (NCDModuleProcess *o, NCDModuleInst *n, NCD_string_id_t template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(template_name >= 0) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(handler_event) + + // init arguments + o->args = args; + o->handler_event = handler_event; + + // set no special functions + o->func_getspecialobj = NULL; + + // set state + set_process_state(o, PROCESS_STATE_INIT); + +#ifndef NDEBUG + // clear interp functions so we can assert they were set + o->interp_func_event = NULL; + o->interp_func_getobj = NULL; +#endif + + // init interpreter part + if (!(n->params->iparams->func_initprocess(n->params->iparams->user, o, template_name))) { + goto fail1; + } + + ASSERT(o->interp_func_event) + ASSERT(o->interp_func_getobj) + + // set state + set_process_state(o, PROCESS_STATE_DOWN); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + return 0; +} + +int NCDModuleProcess_InitValue (NCDModuleProcess *o, NCDModuleInst *n, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(handler_event) + + NCD_string_id_t template_name_id; + + if (NCDVal_IsIdString(template_name)) { + template_name_id = NCDVal_IdStringId(template_name); + } else { + NCDValContString cts; + if (!NCDVal_StringContinuize(template_name, &cts)) { + BLog(BLOG_ERROR, "NCDVal_StringContinuize failed"); + return 0; + } + + template_name_id = NCDStringIndex_GetBin(n->params->iparams->string_index, cts.data, NCDVal_StringLength(template_name)); + NCDValContString_Free(&cts); + if (template_name_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed"); + return 0; + } + } + + return NCDModuleProcess_InitId(o, n, template_name_id, args, handler_event); +} + +void NCDModuleProcess_Free (NCDModuleProcess *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_TERMINATED) +} + +void NCDModuleProcess_AssertFree (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_TERMINATED) +} + +void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj) +{ + DebugObject_Access(&o->d_obj); + + o->func_getspecialobj = func_getspecialobj; +} + +void NCDModuleProcess_Continue (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_DOWN_WAITING) + + set_process_state(o, PROCESS_STATE_DOWN); + + o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_CONTINUE); +} + +void NCDModuleProcess_Terminate (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP || + o->state == PROCESS_STATE_DOWN_WAITING) + + set_process_state(o, PROCESS_STATE_TERMINATING); + + o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_TERMINATE); +} + +int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state != PROCESS_STATE_INIT) + ASSERT(o->state != PROCESS_STATE_TERMINATED) + ASSERT(out_object) + + int res = o->interp_func_getobj(o->interp_user, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +static void process_assert_interp (NCDModuleProcess *o) +{ + // assert that the interpreter knows about the object, and we're not in init + ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP || + o->state == PROCESS_STATE_DOWN_WAITING || o->state == PROCESS_STATE_TERMINATING) +} + +void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, + NCDModuleProcess_interp_func_event interp_func_event, + NCDModuleProcess_interp_func_getobj interp_func_getobj) +{ + ASSERT(o->state == PROCESS_STATE_INIT) + ASSERT(interp_func_event) + ASSERT(interp_func_getobj) + + o->interp_user = interp_user; + o->interp_func_event = interp_func_event; + o->interp_func_getobj = interp_func_getobj; +} + +void NCDModuleProcess_Interp_Up (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_DOWN) + + set_process_state(o, PROCESS_STATE_UP); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_UP); + return; +} + +void NCDModuleProcess_Interp_Down (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_UP) + + set_process_state(o, PROCESS_STATE_DOWN_WAITING); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_DOWN); + return; +} + +void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_TERMINATING) + + set_process_state(o, PROCESS_STATE_TERMINATED); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_TERMINATED); + return; +} + +int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(out_object) + + if (!NCDVal_IsInvalid(o->args)) { + if (name == NCD_STRING_ARGS) { + *out_object = NCDObject_Build(-1, o, process_args_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (name >= NCD_STRING_ARG0 && name <= NCD_STRING_ARG19) { + int num = name - NCD_STRING_ARG0; + if (num < NCDVal_ListCount(o->args)) { + *out_object = NCDObject_BuildFull(-1, o, num, NULL, process_arg_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } + } + + if (!o->func_getspecialobj) { + return 0; + } + + int res = o->func_getspecialobj(o, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +static int process_args_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleProcess *o = NCDObject_DataPtr(obj); + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(!NCDVal_IsInvalid(o->args)) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out_value = NCDVal_NewCopy(mem, o->args); + if (NCDVal_IsInvalid(*out_value)) { + BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDVal_NewCopy failed"); + } + return 1; +} + +static int process_arg_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleProcess *o = NCDObject_DataPtr(obj); + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(!NCDVal_IsInvalid(o->args)) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out_value = NCDVal_NewCopy(mem, NCDVal_ListGet(o->args, NCDObject_DataInt(obj))); + if (NCDVal_IsInvalid(*out_value)) { + BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDVal_NewCopy failed"); + } + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDModule.h b/app/src/main/jni/badvpn/ncd/NCDModule.h new file mode 100644 index 0000000..c00fe90 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDModule.h @@ -0,0 +1,1011 @@ +/** + * @file NCDModule.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDMODULE_H +#define BADVPN_NCD_NCDMODULE_H + +#include + +#include +#include +#include +#include +#include + +#ifndef BADVPN_NO_PROCESS +#include +#endif +#ifndef BADVPN_NO_UDEV +#include +#endif +#ifndef BADVPN_NO_RANDOM +#include +#endif + +#define NCDMODULE_EVENT_UP 1 +#define NCDMODULE_EVENT_DOWN 2 +#define NCDMODULE_EVENT_DOWNUP 3 +#define NCDMODULE_EVENT_DEAD 4 +#define NCDMODULE_EVENT_DEADERROR 5 + +struct NCDModuleInst_s; +struct NCDModuleProcess_s; +struct NCDModuleGroup; +struct NCDInterpModule; +struct NCDInterpModuleGroup; + +/** + * Function called to inform the interpeter of state changes of the + * module instance. + * Possible events are: + * + * - NCDMODULE_EVENT_UP: the instance came up. + * The instance was in down state. + * The instance enters up state. + * + * - NCDMODULE_EVENT_DOWN: the instance went down. + * The instance was in up state. + * The instance enters down state. + * + * After the instance goes down, the interpreter should eventually call + * {@link NCDModuleInst_Clean} or {@link NCDModuleInst_Die}, unless + * the module goes up again. + * + * - NCDMODULE_EVENT_DEAD: the module died. To determine if the module + * died with error, read the is_error member of {@link NCDModuleInst}. + * The instance enters dead state. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. The only exception is that it may free the + * instance from within the NCDMODULE_EVENT_DEAD event. + * + * @param inst the module instance + * @param event event number + */ +typedef void (*NCDModuleInst_func_event) (struct NCDModuleInst_s *inst, int event); + +/** + * Function called when the module instance wants the interpreter to + * resolve an object from the point of view of its statement. + * The instance will not be in dead state. + * This function must not have any side effects. + * + * @param inst the module instance + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_getobj) (struct NCDModuleInst_s *inst, NCD_string_id_t name, NCDObject *out_object); + +/** + * Function called when the module instance wants the interpreter to + * create a new process backend from a process template. + * The instance will not be in dead state. + * + * On success, the interpreter must have called {@link NCDModuleProcess_Interp_SetHandlers} + * from within this function, to allow communication with the controller of the process. + * On success, the new process backend enters down state. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param p handle for the new process backend + * @param template_name name of the template to create the process from, + * as an {@link NCDStringIndex} identifier + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_initprocess) (void *user, struct NCDModuleProcess_s *p, NCD_string_id_t template_name); + +/** + * Function called when the module instance wants the interpreter to + * initiate termination, as if it received an external terminatio request (signal). + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param exit_code exit code to return the the operating system. This overrides any previously + * set exit code, and will be overriden by a signal to the value 1. + * + */ +typedef void (*NCDModuleInst_func_interp_exit) (void *user, int exit_code); + +/** + * Function called when the module instance wants the interpreter to + * provide its extra command line arguments. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param mem value memory to use + * @param out_value write value reference here on success + * @return 1 if available, 0 if not available. If available, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModuleInst_func_interp_getargs) (void *user, NCDValMem *mem, NCDValRef *out_value); + +/** + * Function called when the module instance wants the interpreter to + * provide its retry time. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @return retry time in milliseconds + */ +typedef btime_t (*NCDModuleInst_func_interp_getretrytime) (void *user); + +/** + * Function called when the module instance wants the interpreter to + * load a new module group. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param group module group to load + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_interp_loadgroup) (void *user, const struct NCDModuleGroup *group); + +#define NCDMODULEPROCESS_EVENT_UP 1 +#define NCDMODULEPROCESS_EVENT_DOWN 2 +#define NCDMODULEPROCESS_EVENT_TERMINATED 3 + +/** + * Handler which reports process state changes from the interpreter. + * Possible events are: + * + * - NCDMODULEPROCESS_EVENT_UP: the process went up. + * The process was in down state. + * The process enters up state. + * + * - NCDMODULEPROCESS_EVENT_DOWN: the process went down. + * The process was in up state. + * The process enters waiting state. + * + * NOTE: the process enters waiting state, NOT down state, and is paused. + * To allow the process to continue, call {@link NCDModuleProcess_Continue}. + * + * - NCDMODULEPROCESS_EVENT_TERMINATED: the process terminated. + * The process was in terminating state. + * The process enters terminated state. + * + * @param user pointer to the process. Use {@link UPPER_OBJECT} to retrieve the pointer + * to the containing struct. + * @param event event number + */ +typedef void (*NCDModuleProcess_handler_event) (struct NCDModuleProcess_s *process, int event); + +/** + * Function called when the interpreter wants to resolve a special + * object in the process. + * This function must have no side effects. + * + * @param user pointer to the process. Use {@link UPPER_OBJECT} to retrieve the pointer + * to the containing struct. + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleProcess_func_getspecialobj) (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object); + +#define NCDMODULEPROCESS_INTERP_EVENT_CONTINUE 1 +#define NCDMODULEPROCESS_INTERP_EVENT_TERMINATE 2 + +/** + * Function called to report process backend requests to the interpreter. + * Possible events are: + * + * - NCDMODULEPROCESS_INTERP_EVENT_CONTINUE: the process can continue. + * The process backend was in waiting state. + * The process backend enters down state. + * + * - NCDMODULEPROCESS_INTERP_EVENT_TERMINATE: the process should terminate. + * The process backend was in down, up or waiting state. + * The process backend enters terminating state. + * + * The interpreter should call {@link NCDModuleProcess_Interp_Terminated} + * when the process terminates. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. + * + * @param user as in {@link NCDModuleProcess_Interp_SetHandlers} + * @param event event number + */ +typedef void (*NCDModuleProcess_interp_func_event) (void *user, int event); + +/** + * Function called to have the interpreter resolve an object within the process + * of a process backend. + * This function must not have any side effects. + * + * @param user as in {@link NCDModuleProcess_Interp_SetHandlers} + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 in failure + */ +typedef int (*NCDModuleProcess_interp_func_getobj) (void *user, NCD_string_id_t name, NCDObject *out_object); + +struct NCDModule; + +/** + * Contains parameters to the module initialization function + * ({@link NCDModule_func_new2}) that are passed indirectly. + */ +struct NCDModuleInst_new_params { + /** + * A reference to the argument list for the module instance. + * The reference remains valid as long as the backend instance + * exists. Unless the module has the NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + * flag set, it is guaranteed that any strings within the arguments will be + * some kind of ContinuousString. + */ + NCDValRef args; + + /** + * If the module instance corresponds to a method-like statement, + * this pointer identifies the object it is being invoked with. + * If the object is a statement (i.e. a {@link NCDModuleInst}), then this + * will be the NCDModuleInst pointer, and {@link NCDModuleInst_Backend_GetUser} + * can be called on this to retrieve the pointer to preallocated memory for + * the backend instance; *unless* {@link NCDModuleInst_Backend_PassMemToMethods} + * was called for the object on which the method is being called, in which case + * this will directly point to the preallocated memory. + * On the other hand, if this is a method on an internal object built using + * only {@link NCDObject_Build} or {@link NCDObject_BuildFull}, + * this pointer will be whatever was passed as the "data_ptr" argument, for the + * first function, and as "method_user", for the latter function. + */ + void *method_user; +}; + +/** + * Contains parameters to {@link NCDModuleInst_Init} that are passed indirectly. + * This itself only contains parameters related to communication between the + * backend and the creator of the module instance; other parameters are passed + * via the iparams member; + */ +struct NCDModuleInst_params { + /** + * Callback to report state changes. + */ + NCDModuleInst_func_event func_event; + /** + * Callback to resolve objects from the viewpoint of the instance. + */ + NCDModuleInst_func_getobj func_getobj; + /** + * Log function which appends a log prefix with {@link BLog_Append}. + */ + BLog_logfunc logfunc; + /** + * Pointer to an {@link NCDModuleInst_iparams} structure, which exposes + * services provided by the interpreter. + */ + const struct NCDModuleInst_iparams *iparams; +}; + +/** + * Contains parameters to {@link NCDModuleInst_Init} that are passed indirectly. + * This only contains parameters related to services provided by the interpreter. + */ +struct NCDModuleInst_iparams { + /** + * Reactor we live in. + */ + BReactor *reactor; +#ifndef BADVPN_NO_PROCESS + /** + * Process manager. + */ + BProcessManager *manager; +#endif +#ifndef BADVPN_NO_UDEV + /** + * Udev manager. + */ + NCDUdevManager *umanager; +#endif +#ifndef BADVPN_NO_RANDOM + /** + * Random number generator. + */ + BRandom2 *random2; +#endif + /** + * String index which keeps a mapping between strings and string identifiers. + */ + NCDStringIndex *string_index; + /** + * Pointer passed to the interpreter callbacks below, for state keeping. + */ + void *user; + /** + * Callback to create a new template process. + */ + NCDModuleInst_func_initprocess func_initprocess; + /** + * Callback to request interpreter termination. + */ + NCDModuleInst_func_interp_exit func_interp_exit; + /** + * Callback to get extra command line arguments. + */ + NCDModuleInst_func_interp_getargs func_interp_getargs; + /** + * Callback to get retry time. + */ + NCDModuleInst_func_interp_getretrytime func_interp_getretrytime; + /** + * Callback to load a module group. + */ + NCDModuleInst_func_interp_loadgroup func_loadgroup; +}; + +/** + * Module instance. + * The module instance is initialized by the interpreter by calling + * {@link NCDModuleInst_Init}. It is implemented by a module backend + * specified in a {@link NCDModule}. + */ +typedef struct NCDModuleInst_s { + const struct NCDInterpModule *m; + const struct NCDModuleInst_params *params; + void *mem; // not modified by NCDModuleInst (but passed to module) + unsigned int state:3; + unsigned int pass_mem_to_methods:1; + unsigned int istate:3; // untouched by NCDModuleInst + DebugObject d_obj; +} NCDModuleInst; + +/** + * Process created from a process template on behalf of a module backend + * instance, implemented by the interpreter. + */ +typedef struct NCDModuleProcess_s { + NCDValRef args; + NCDModuleProcess_handler_event handler_event; + NCDModuleProcess_func_getspecialobj func_getspecialobj; + void *interp_user; + NCDModuleProcess_interp_func_event interp_func_event; + NCDModuleProcess_interp_func_getobj interp_func_getobj; +#ifndef NDEBUG + int state; +#endif + DebugObject d_obj; +} NCDModuleProcess; + +/** + * Initializes an instance of an NCD module. + * The instance is initialized in down state. + * WARNING: this directly calls the module backend; expect to be called back + * + * This and other non-Backend methods are the interpreter interface. + * The Backend methods are the module backend interface and are documented + * independently with their own logical states. + * + * NOTE: the instance structure \a n should have the member 'mem' initialized + * to point to preallocated memory for the statement. This memory must be + * at least m->prealloc_size big and must be properly aligned for any object. + * The 'mem' pointer is never modified by NCDModuleInst, so that the interpreter + * can use it as outside the lifetime of NCDModuleInst. + * + * @param n the instance + * @param m pointer to the {@link NCDInterpModule} structure representing the module + * to be instantiated + * @param method_context a context pointer passed to the module backend, applicable to method-like + * statements only. This should be set to the 'user' member of the + * {@link NCDObject} which represents the base object for the method. + * The caller must ensure that the NCDObject that was used is of the type + * expected by the module being instanciated. + * @param args arguments to the module. Must be a list value. Must be available and unchanged + * as long as the instance exists. Unless the module has the + * NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS flag set, any strings within the + * arguments must be some kind of ContinuousString. This can be ensured by calling + * {@link NCDValMem_ConvertNonContinuousStrings}. + * @param user argument to callback functions + * @param params more parameters, see {@link NCDModuleInst_params} + */ +void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void *method_context, NCDValRef args, const struct NCDModuleInst_params *params); + +/** + * Frees the instance. + * The instance must be in dead state. + * + * @param n the instance + */ +void NCDModuleInst_Free (NCDModuleInst *n); + +/** + * Requests the instance to die. + * The instance must be in down or up state. + * The instance enters dying state. + * WARNING: this directly calls the module backend; expect to be called back + * + * @param n the instance + */ +void NCDModuleInst_Die (NCDModuleInst *n); + +/** + * Attempts to destroy the instance immediately. + * This function can be used to optimize destroying instances of modules which + * don't specify any {@link NCDModule_func_die} handler. If immediate destruction + * is not possible, this does nothing and returns 0; {@link NCDModuleInst_Die} + * should be used to destroy the instance instead. If however immediate destruction + * is possible, this destroys the module instance and returns 1; {@link NCDModuleInst_Free} + * must not be called after that. + * The instance must be in down or up state, as for {@link NCDModuleInst_Die}. + * + * @param n the instance + * @return 1 if destruction was performed, 0 if not + */ +int NCDModuleInst_TryFree (NCDModuleInst *n); + +/** + * Informs the module that it is in a clean state to proceed. + * The instance must be in down state. + * WARNING: this directly calls the module backend; expect to be called back + * + * @param n the instance + */ +void NCDModuleInst_Clean (NCDModuleInst *n); + +/** + * Returns an {@link NCDObject} which can be used to resolve variables and objects + * within this instance, as well as call its methods. The resulting object may only + * be used immediately, and becomes invalid when the instance is freed. + * + * @param n the instance + * @return an NCDObject for this instance + */ +NCDObject NCDModuleInst_Object (NCDModuleInst *n); + +/** + * If this is called, any methods called on this object will receive the preallocated + * memory pointer as the object state pointer. This means that in the + * {@link NCDModule_func_getvar2} function which is called when a method is created, + * the preallocated memory should be accessed as params->method_user. + * By default, however, params->method_user points to the NCDModuleInst of the base + * object, and {@link NCDModuleInst_Backend_GetUser} is needed to retrieve the + * preallocated memory pointer. + */ +void NCDModuleInst_Backend_PassMemToMethods (NCDModuleInst *n); + +/** + * Retuns the state pointer passed to handlers of a module backend instance; + * see {@link NCDModule_func_new2}. + * + * @param n backend instance handle + * @return argument passed to handlers + */ +void * NCDModuleInst_Backend_GetUser (NCDModuleInst *n); + +/** + * Puts the backend instance into up state. + * The instance must be in down state. + * The instance enters up state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Up (NCDModuleInst *n); + +/** + * Puts the backend instance into down state. + * The instance must be in up state. + * The instance enters down state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Down (NCDModuleInst *n); + +/** + * Puts the backend instance into down state, then immediatly back into the up state. + * This effectively causes the interpreter to start backtracking to this statement. + * The instance must be in up state, and remains in up state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_DownUp (NCDModuleInst *n); + +/** + * Destroys the backend instance. + * The backend instance handle becomes invalid and must not be used from + * the backend any longer. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Dead (NCDModuleInst *n); + +/** + * Like {@link NCDModuleInst_Backend_Dead}, but also reports an error condition + * to the interpreter. + */ +void NCDModuleInst_Backend_DeadError (NCDModuleInst *n); + +/** + * Resolves an object for a backend instance, from the point of the instance's + * statement in the containing process. + * + * @param n backend instance handle + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Logs a backend instance message. + * + * @param n backend instance handle + * @param channel log channel + * @param level loglevel + * @param fmt format string as in printf, arguments follow + */ +void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...); + +/** + * Like {@link NCDModuleInst_Backend_Log}, but the extra arguments are passed + * as a va_list. This allows creation of logging wrappers. + */ +void NCDModuleInst_Backend_LogVarArg (NCDModuleInst *n, int channel, int level, const char *fmt, va_list vl); + +/** + * Returns a logging context. The context is valid until the backend dies. + */ +BLogContext NCDModuleInst_Backend_LogContext (NCDModuleInst *n); + +/** + * Initiates interpreter termination. + * + * @param n backend instance handle + * @param exit_code exit code to return to the operating system. This overrides + * any previously set exit code, and will be overriden by a + * termination signal to the value 1. + */ +void NCDModuleInst_Backend_InterpExit (NCDModuleInst *n, int exit_code); + +/** + * Retrieves extra command line arguments passed to the interpreter. + * + * @param n backend instance handle + * @param mem value memory to use + * @param out_value the arguments will be written here on success as a list value + * @return 1 if available, 0 if not available. If available, but out of memory, returns 1 + * and an invalid value. + */ +int NCDModuleInst_Backend_InterpGetArgs (NCDModuleInst *n, NCDValMem *mem, NCDValRef *out_value); + +/** + * Returns the retry time of the intepreter. + * + * @param n backend instance handle + * @return retry time in milliseconds + */ +btime_t NCDModuleInst_Backend_InterpGetRetryTime (NCDModuleInst *n); + +/** + * Loads a module group into the interpreter. + * + * @param n backend instance handle + * @param group module group to load + * @return 1 on success, 0 on failure + */ +int NCDModuleInst_Backend_InterpLoadGroup (NCDModuleInst *n, const struct NCDModuleGroup *group); + +/** + * Initializes a process in the interpreter from a process template. + * This must be called on behalf of a module backend instance. + * The process is initializes in down state. + * + * @param o the process + * @param n backend instance whose interpreter will be providing the process + * @param template_name name of the process template as an {@link NCDStringIndex} identifier + * @param args arguments to the process. Must be an invalid value or a list value. + * The value must be available and unchanged while the process exists. + * @param handler_event handler which reports events about the process from the + * interpreter + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_InitId (NCDModuleProcess *o, NCDModuleInst *n, NCD_string_id_t template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) WARN_UNUSED; + +/** + * Wrapper around {@link NCDModuleProcess_InitId} which takes the template name as an + * {@link NCDValRef}, which must point to a string value. + */ +int NCDModuleProcess_InitValue (NCDModuleProcess *o, NCDModuleInst *n, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) WARN_UNUSED; + +/** + * Frees the process. + * The process must be in terminated state. + * + * @param o the process + */ +void NCDModuleProcess_Free (NCDModuleProcess *o); + +/** + * Does nothing. + * The process must be in terminated state. + * + * @param o the process + */ +void NCDModuleProcess_AssertFree (NCDModuleProcess *o); + +/** + * Sets callback functions for providing special objects within the process. + * + * @param o the process + * @param func_getspecialobj function for resolving special objects, or NULL + */ +void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj); + +/** + * Continues the process after the process went down. + * The process must be in waiting state. + * The process enters down state. + * + * @param o the process + */ +void NCDModuleProcess_Continue (NCDModuleProcess *o); + +/** + * Requests the process to terminate. + * The process must be in down, up or waiting state. + * The process enters terminating state. + * + * @param o the process + */ +void NCDModuleProcess_Terminate (NCDModuleProcess *o); + +/** + * Resolves an object within the process from the point + * at the end of the process. + * This function has no side effects. + * + * @param o the process + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Sets callback functions for the interpreter to implement the + * process backend. + * Must be called from within {@link NCDModuleInst_func_initprocess} + * if success is to be reported there. + * + * @param o process backend handle, as in {@link NCDModuleInst_func_initprocess} + * @param interp_user argument to callback functions + * @param interp_func_event function for reporting continue/terminate requests + * @param interp_func_getobj function for resolving objects within the process + */ +void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, + NCDModuleProcess_interp_func_event interp_func_event, + NCDModuleProcess_interp_func_getobj interp_func_getobj); + +/** + * Reports the process backend as up. + * The process backend must be in down state. + * The process backend enters up state. + * WARNING: this directly calls the process creator; expect to be called back + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Up (NCDModuleProcess *o); + +/** + * Reports the process backend as down. + * The process backend must be in up state. + * The process backend enters waiting state. + * WARNING: this directly calls the process creator; expect to be called back + * + * NOTE: the backend enters waiting state, NOT down state. The interpreter should + * pause the process until {@link NCDModuleProcess_interp_func_event} reports + * NCDMODULEPROCESS_INTERP_EVENT_CONTINUE, unless termination is requested via + * NCDMODULEPROCESS_INTERP_EVENT_TERMINATE. + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Down (NCDModuleProcess *o); + +/** + * Reports termination of the process backend. + * The process backend must be in terminating state. + * The process backend handle becomes invalid and must not be used + * by the interpreter any longer. + * WARNING: this directly calls the process creator; expect to be called back + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o); + +/** + * Resolves a special process object for the process backend. + * + * @param o process backend handle + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Function called before any instance of any backend in a module + * group is created; + * + * @param params structure containing global resources, such as + * {@link BReactor}, {@link BProcessManager} and {@link NCDUdevManager}. + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModule_func_globalinit) (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params); + +/** + * Function called to clean up after {@link NCDModule_func_globalinit} and modules + * in a module group. + * There are no backend instances alive from this module group. + */ +typedef void (*NCDModule_func_globalfree) (struct NCDInterpModuleGroup *group); + +/** + * Handler called to create an new module backend instance. + * The backend is initialized in down state. + * + * If the backend fails initialization, this function should report the backend + * instance to have died with error by calling {@link NCDModuleInst_Backend_DeadError}. + * + * @param o if the module specifies a positive alloc_size value in the {@link NCDModule} + * structure, this will point to the allocated memory that can be used by the + * module instance while it exists. If the alloc_size is 0 (default), this may or + * may not be NULL. + * @param i module backend instance handler. The backend may only use this handle via + * the Backend functions of {@link NCDModuleInst}. + */ +typedef void (*NCDModule_func_new2) (void *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params); + +/** + * Handler called to request termination of a backend instance. + * The backend instance was in down or up state. + * The backend instance enters dying state. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + */ +typedef void (*NCDModule_func_die) (void *o); + +/** + * Function called to resolve a variable within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the variable to resolve + * @param mem value memory to use + * @param out on success, the backend should initialize the value here + * @return 1 if exists, 0 if not exists. If exists, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValMem *mem, NCDValRef *out); + +/** + * Function called to resolve a variable within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the variable to resolve as an {@link NCDStringIndex} identifier + * @param mem value memory to use + * @param out on success, the backend should initialize the value here + * @return 1 if exists, 0 if not exists. If exists, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModule_func_getvar2) (void *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); + +/** + * Function called to resolve an object within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModule_func_getobj) (void *o, NCD_string_id_t name, NCDObject *out_object); + +/** + * Handler called when the module instance is in a clean state. + * This means that all statements preceding it in the process are + * up, this statement is down, and all following statements are + * uninitialized. When a backend instance goes down, it is guaranteed, + * as long as it stays down, that either this will be called or + * termination will be requested with {@link NCDModule_func_die}. + * The backend instance was in down state. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + */ +typedef void (*NCDModule_func_clean) (void *o); + +#define NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN (1 << 0) +#define NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS (1 << 1) + +/** + * Structure encapsulating the implementation of a module backend. + */ +struct NCDModule { + /** + * If this implements a plain statement, the name of the statement. + * If this implements a method, then "base_type::method_name". + */ + const char *type; + + /** + * The base type for methods operating on instances of this backend. + * Any module with type of form "base_type::method_name" is considered + * a method of instances of this backend. + * If this is NULL, the base type will default to type. + */ + const char *base_type; + + /** + * Function called to create an new backend instance. + */ + NCDModule_func_new2 func_new2; + + /** + * Function called to request termination of a backend instance. + * May be NULL, in which case the default is to call NCDModuleInst_Backend_Dead(). + */ + NCDModule_func_die func_die; + + /** + * Function called to resolve a variable within the backend instance. + * May be NULL. + */ + NCDModule_func_getvar func_getvar; + + /** + * Function called to resolve a variable within the backend instance. + * May be NULL. + */ + NCDModule_func_getvar2 func_getvar2; + + /** + * Function called to resolve an object within the backend instance. + * May be NULL. + */ + NCDModule_func_getobj func_getobj; + + /** + * Function called when the backend instance is in a clean state. + * May be NULL. + */ + NCDModule_func_clean func_clean; + + /** + * Various flags. + * + * - NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN + * Whether the interpreter is allowed to call func_getvar and func_getobj + * even when the backend instance is in down state (as opposed to just + * in up state. + * + * - NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + * If not set, strings within arguments which are not some kind of ContinuousString + * will be converted to some kind of ContinuousString before the module's init + * function is called. If set, they will not be, and the module must work with any + * kind of strings (i.e. {@link NCDVal_StringData} may not be allowed). + */ + int flags; + + /** + * The amount of memory to preallocate for each module instance. + * Preallocation can be used to avoid having to allocate memory from + * module initialization. The memory can be accessed via the first + * argument to {@link NCDModule_func_new2} and other calls. + */ + int alloc_size; +}; + +/** + * Structure encapsulating a group of module backend implementations, + * with global init and free functions. + */ +struct NCDModuleGroup { + /** + * Function called before any instance of any module backend in this + * group is crated. May be NULL. + */ + NCDModule_func_globalinit func_globalinit; + + /** + * Function called to clean up after {@link NCDModule_func_globalinit}. + * May be NULL. + */ + NCDModule_func_globalfree func_globalfree; + + /** + * Array of module backends. The array must be terminated with a + * structure that has a NULL type member. + */ + const struct NCDModule *modules; + + /** + * A pointer to an array of strings which will be mapped to + * {@link NCDStringIndex} string identifiers for the module to use. + * The array must be terminated by NULL. The resulting string + * identifiers will be available in the 'strings' member in + * {@link NCDInterpModuleGroup}. + */ + const char *const*strings; +}; + +/** + * Represents an {@link NCDModule} within an interpreter. + * This structure is initialized by the interpreter when it loads a module group. + */ +struct NCDInterpModule { + /** + * A copy of the original NCDModule structure, + */ + struct NCDModule module; + + /** + * The string identifier of this module's base_type (or type if base_type is + * not specified) according to {@link NCDStringIndex}. + */ + NCD_string_id_t base_type_id; + + /** + * A pointer to the {@link NCDInterpModuleGroup} representing the group + * this module belongs to. + */ + struct NCDInterpModuleGroup *group; +}; + +/** + * Represents an {@link NCDModuleGroup} within an interpreter. + * This structure is initialized by the interpreter when it loads a module group. + */ +struct NCDInterpModuleGroup { + /** + * A copy of the original NCDModuleGroup structure. + */ + struct NCDModuleGroup group; + + /** + * An array of string identifiers corresponding to the strings + * in the 'strings' member of NCDModuleGroup. May be NULL if there + * are no strings in the NCDModuleGroup. + */ + NCD_string_id_t *strings; + + /** + * Pointer which allows the module to keep private interpreter-wide state. + * This can be freely modified by the module; the interpeter will not + * read or write it. + */ + void *group_state; +}; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDModuleIndex.c b/app/src/main/jni/badvpn/ncd/NCDModuleIndex.c new file mode 100644 index 0000000..12ef48a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDModuleIndex.c @@ -0,0 +1,372 @@ +/** + * @file NCDModuleIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "NCDModuleIndex.h" + +#include + +#include "NCDModuleIndex_mhash.h" +#include + +static int string_pointer_comparator (void *user, void *v1, void *v2) +{ + const char **s1 = v1; + const char **s2 = v2; + int cmp = strcmp(*s1, *s2); + return B_COMPARE(cmp, 0); +} + +static struct NCDModuleIndex_module * find_module (NCDModuleIndex *o, const char *type) +{ + NCDModuleIndex__MHashRef ref = NCDModuleIndex__MHash_Lookup(&o->modules_hash, 0, type); + ASSERT(!ref.link || !strcmp(ref.link->imodule.module.type, type)) + return ref.link; +} + +#ifndef NDEBUG +static struct NCDModuleIndex_base_type * find_base_type (NCDModuleIndex *o, const char *base_type) +{ + BAVLNode *node = BAVL_LookupExact(&o->base_types_tree, &base_type); + if (!node) { + return NULL; + } + + struct NCDModuleIndex_base_type *bt = UPPER_OBJECT(node, struct NCDModuleIndex_base_type, base_types_tree_node); + ASSERT(!strcmp(bt->base_type, base_type)) + + return bt; +} +#endif + +static int add_method (const char *type, const struct NCDInterpModule *module, NCDMethodIndex *method_index, int *out_method_id) +{ + ASSERT(type) + ASSERT(module) + ASSERT(method_index) + ASSERT(out_method_id) + + const char search[] = "::"; + size_t search_len = sizeof(search) - 1; + + size_t table[sizeof(search) - 1]; + build_substring_backtrack_table_reverse(search, search_len, table); + + size_t pos; + if (!find_substring_reverse(type, strlen(type), search, search_len, table, &pos)) { + *out_method_id = -1; + return 1; + } + + ASSERT(pos >= 0) + ASSERT(pos <= strlen(type) - search_len) + ASSERT(!memcmp(type + pos, search, search_len)) + + int method_id = NCDMethodIndex_AddMethod(method_index, type, pos, type + pos + search_len, module); + if (method_id < 0) { + BLog(BLOG_ERROR, "NCDMethodIndex_AddMethod failed"); + return 0; + } + + *out_method_id = method_id; + return 1; +} + +int NCDModuleIndex_Init (NCDModuleIndex *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + // init modules hash + if (!NCDModuleIndex__MHash_Init(&o->modules_hash, NCDMODULEINDEX_MODULES_HASH_SIZE)) { + BLog(BLOG_ERROR, "NCDModuleIndex__MHash_Init failed"); + goto fail0; + } + +#ifndef NDEBUG + // init base types tree + BAVL_Init(&o->base_types_tree, OFFSET_DIFF(struct NCDModuleIndex_base_type, base_type, base_types_tree_node), string_pointer_comparator, NULL); +#endif + + // init groups list + LinkedList0_Init(&o->groups_list); + + // init method index + if (!NCDMethodIndex_Init(&o->method_index, string_index)) { + BLog(BLOG_ERROR, "NCDMethodIndex_Init failed"); + goto fail1; + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + NCDModuleIndex__MHash_Free(&o->modules_hash); +fail0: + return 0; +} + +void NCDModuleIndex_Free (NCDModuleIndex *o) +{ + DebugObject_Free(&o->d_obj); + + // free groups + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->groups_list)) { + struct NCDModuleIndex_group *ig = UPPER_OBJECT(ln, struct NCDModuleIndex_group, groups_list_node); + if (ig->igroup.group.func_globalfree) { + ig->igroup.group.func_globalfree(&ig->igroup); + } + BFree(ig->igroup.strings); + LinkedList0_Remove(&o->groups_list, &ig->groups_list_node); + BFree(ig); + } + +#ifndef NDEBUG + // free base types + BAVLNode *tn; + while (tn = BAVL_GetFirst(&o->base_types_tree)) { + struct NCDModuleIndex_base_type *bt = UPPER_OBJECT(tn, struct NCDModuleIndex_base_type, base_types_tree_node); + BAVL_Remove(&o->base_types_tree, &bt->base_types_tree_node); + BFree(bt); + } +#endif + + // free method index + NCDMethodIndex_Free(&o->method_index); + + // free modules hash + NCDModuleIndex__MHash_Free(&o->modules_hash); +} + +int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, const struct NCDModuleInst_iparams *iparams, NCDStringIndex *string_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(group) + ASSERT(iparams) + ASSERT(string_index) + + // count modules in the group + size_t num_modules = 0; + while (group->modules[num_modules].type) { + num_modules++; + } + + // compute allocation size + bsize_t size = bsize_add(bsize_fromsize(sizeof(struct NCDModuleIndex_group)), bsize_mul(bsize_fromsize(num_modules), bsize_fromsize(sizeof(struct NCDModuleIndex_module)))); + + // allocate group + struct NCDModuleIndex_group *ig = BAllocSize(size); + if (!ig) { + BLog(BLOG_ERROR, "BAllocSize failed"); + goto fail0; + } + + // insert to groups list + LinkedList0_Prepend(&o->groups_list, &ig->groups_list_node); + + // copy NCDModuleGroup + ig->igroup.group = *group; + + if (!group->strings) { + // not resolving strings + ig->igroup.strings = NULL; + } else { + // compute number of strings + size_t num_strings = 0; + while (group->strings[num_strings]) { + num_strings++; + } + + // allocate array for string IDs + ig->igroup.strings = BAllocArray(num_strings, sizeof(ig->igroup.strings[0])); + if (!ig->igroup.strings) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + // map strings to IDs + for (size_t i = 0; i < num_strings; i++) { + ig->igroup.strings[i] = NCDStringIndex_Get(string_index, group->strings[i]); + if (ig->igroup.strings[i] < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail2; + } + } + } + + // call group init function + if (group->func_globalinit) { + if (!group->func_globalinit(&ig->igroup, iparams)) { + BLog(BLOG_ERROR, "func_globalinit failed"); + goto fail2; + } + } + + size_t num_inited_modules = 0; + + // initialize modules + for (size_t i = 0; i < num_modules; i++) { + const struct NCDModule *nm = &group->modules[i]; + struct NCDModuleIndex_module *m = &ig->modules[i]; + + // make sure a module with this name doesn't exist already + if (find_module(o, nm->type)) { + BLog(BLOG_ERROR, "module type '%s' already exists", nm->type); + goto loop_fail0; + } + + // copy NCDModule structure + m->imodule.module = *nm; + + // determine base type + const char *base_type = (nm->base_type ? nm->base_type : nm->type); + ASSERT(base_type) + + // map base type to ID + m->imodule.base_type_id = NCDStringIndex_Get(string_index, base_type); + if (m->imodule.base_type_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + + // set group pointer + m->imodule.group = &ig->igroup; + + // register method + if (!add_method(nm->type, &m->imodule, &o->method_index, &m->method_id)) { + goto loop_fail0; + } + +#ifndef NDEBUG + // ensure that this base_type does not appear in any other groups + struct NCDModuleIndex_base_type *bt = find_base_type(o, base_type); + if (bt) { + if (bt->group != ig) { + BLog(BLOG_ERROR, "module base type '%s' already exists in another module group", base_type); + goto loop_fail1; + } + } else { + if (!(bt = BAlloc(sizeof(*bt)))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto loop_fail1; + } + bt->base_type = base_type; + bt->group = ig; + ASSERT_EXECUTE(BAVL_Insert(&o->base_types_tree, &bt->base_types_tree_node, NULL)) + } +#endif + + // insert to modules hash + NCDModuleIndex__MHashRef ref = {m, m}; + int res = NCDModuleIndex__MHash_Insert(&o->modules_hash, 0, ref, NULL); + ASSERT_EXECUTE(res) + + num_inited_modules++; + continue; + +#ifndef NDEBUG + loop_fail1: + if (m->method_id >= 0) { + NCDMethodIndex_RemoveMethod(&o->method_index, m->method_id); + } +#endif + loop_fail0: + goto fail3; + } + + return 1; + +fail3: + while (num_inited_modules-- > 0) { + struct NCDModuleIndex_module *m = &ig->modules[num_inited_modules]; + NCDModuleIndex__MHashRef ref = {m, m}; + NCDModuleIndex__MHash_Remove(&o->modules_hash, 0, ref); +#ifndef NDEBUG + const struct NCDModule *nm = &group->modules[num_inited_modules]; + const char *base_type = (nm->base_type ? nm->base_type : nm->type); + struct NCDModuleIndex_base_type *bt = find_base_type(o, base_type); + if (bt) { + ASSERT(bt->group == ig) + BAVL_Remove(&o->base_types_tree, &bt->base_types_tree_node); + BFree(bt); + } +#endif + if (m->method_id >= 0) { + NCDMethodIndex_RemoveMethod(&o->method_index, m->method_id); + } + } + if (group->func_globalfree) { + group->func_globalfree(&ig->igroup); + } +fail2: + BFree(ig->igroup.strings); +fail1: + LinkedList0_Remove(&o->groups_list, &ig->groups_list_node); + BFree(ig); +fail0: + return 0; +} + +const struct NCDInterpModule * NCDModuleIndex_FindModule (NCDModuleIndex *o, const char *type) +{ + DebugObject_Access(&o->d_obj); + ASSERT(type) + + struct NCDModuleIndex_module *m = find_module(o, type); + if (!m) { + return NULL; + } + + return &m->imodule; +} + +int NCDModuleIndex_GetMethodNameId (NCDModuleIndex *o, const char *method_name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(method_name) + + return NCDMethodIndex_GetMethodNameId(&o->method_index, method_name); +} + +const struct NCDInterpModule * NCDModuleIndex_GetMethodModule (NCDModuleIndex *o, NCD_string_id_t obj_type, int method_name_id) +{ + DebugObject_Access(&o->d_obj); + + return NCDMethodIndex_GetMethodModule(&o->method_index, obj_type, method_name_id); +} diff --git a/app/src/main/jni/badvpn/ncd/NCDModuleIndex.h b/app/src/main/jni/badvpn/ncd/NCDModuleIndex.h new file mode 100644 index 0000000..f7cc255 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDModuleIndex.h @@ -0,0 +1,86 @@ +/** + * @file NCDModuleIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDMODULEINDEX_H +#define BADVPN_NCDMODULEINDEX_H + +#include +#include +#include +#include +#include +#include +#include + +#define NCDMODULEINDEX_MODULES_HASH_SIZE 512 + +struct NCDModuleIndex_module { + struct NCDInterpModule imodule; + struct NCDModuleIndex_module *hash_next; + int method_id; +}; + +#ifndef NDEBUG +struct NCDModuleIndex_base_type { + const char *base_type; + struct NCDModuleIndex_group *group; + BAVLNode base_types_tree_node; +}; +#endif + +struct NCDModuleIndex_group { + LinkedList0Node groups_list_node; + struct NCDInterpModuleGroup igroup; + struct NCDModuleIndex_module modules[]; +}; + +typedef struct NCDModuleIndex_module *NCDModuleIndex__mhash_link; +typedef const char *NCDModuleIndex__mhash_key; + +#include "NCDModuleIndex_mhash.h" +#include + +typedef struct { + NCDModuleIndex__MHash modules_hash; +#ifndef NDEBUG + BAVL base_types_tree; +#endif + LinkedList0 groups_list; + NCDMethodIndex method_index; + DebugObject d_obj; +} NCDModuleIndex; + +int NCDModuleIndex_Init (NCDModuleIndex *o, NCDStringIndex *string_index) WARN_UNUSED; +void NCDModuleIndex_Free (NCDModuleIndex *o); +int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, const struct NCDModuleInst_iparams *iparams, NCDStringIndex *string_index) WARN_UNUSED; +const struct NCDInterpModule * NCDModuleIndex_FindModule (NCDModuleIndex *o, const char *type); +int NCDModuleIndex_GetMethodNameId (NCDModuleIndex *o, const char *method_name); +const struct NCDInterpModule * NCDModuleIndex_GetMethodModule (NCDModuleIndex *o, NCD_string_id_t obj_type, int method_name_id); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDModuleIndex_mhash.h b/app/src/main/jni/badvpn/ncd/NCDModuleIndex_mhash.h new file mode 100644 index 0000000..8057b39 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDModuleIndex_mhash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDModuleIndex__MHash +#define CHASH_PARAM_ENTRY struct NCDModuleIndex_module +#define CHASH_PARAM_LINK NCDModuleIndex__mhash_link +#define CHASH_PARAM_KEY NCDModuleIndex__mhash_key +#define CHASH_PARAM_ARG int +#define CHASH_PARAM_NULL ((NCDModuleIndex__mhash_link)NULL) +#define CHASH_PARAM_DEREF(arg, link) (link) +#define CHASH_PARAM_ENTRYHASH(arg, entry) (badvpn_djb2_hash((const uint8_t *)(entry).ptr->imodule.module.type)) +#define CHASH_PARAM_KEYHASH(arg, key) (badvpn_djb2_hash((const uint8_t *)(key))) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->imodule.module.type, (entry2).ptr->imodule.module.type)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->imodule.module.type)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/app/src/main/jni/badvpn/ncd/NCDObject.c b/app/src/main/jni/badvpn/ncd/NCDObject.c new file mode 100644 index 0000000..c2f4cad --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDObject.c @@ -0,0 +1,40 @@ +/** + * @file NCDObject.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "NCDObject.h" + +int NCDObject_no_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + return 0; +} + +int NCDObject_no_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + return 0; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDObject.h b/app/src/main/jni/badvpn/ncd/NCDObject.h new file mode 100644 index 0000000..237ec72 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDObject.h @@ -0,0 +1,356 @@ +/** + * @file NCDObject.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDOBJECT_H +#define BADVPN_NCDOBJECT_H + +#include + +#include +#include +#include +#include + +/** + * Represents an NCD object. + * Objects expose the following functionalities: + * - resolving variables by name, + * - resolving objects by name, + * - provide information for calling method-like statements. + * + * The NCDObject structure must not be stored persistently; it is only + * valid at the time it was obtained, and any change of state in the + * execution of the NCD program may render the object invalid. + * However, the structure does not contain any resources, and can freely + * be passed around by value. + */ +typedef struct NCDObject_s NCDObject; + +/** + * Callback function for variable resolution requests. + * + * @param obj const pointer to the NCDObject this is being called for. + * {@link NCDObject_DataPtr} and {@link NCDObject_DataInt} can be + * used to retrieve state information which was passed to + * {@link NCDObject_Build} or {@link NCDObject_BuildFull}. + * @param name name of the variable being resolved, in form of an {@link NCDStringIndex} + * string identifier + * @param mem pointer to the memory object where the resulting value should be + * constructed + * @param out_value If the variable exists, *out_value should be set to the value + * reference to the result value. If the variable exists but there + * was an error constructing the value, should be set to an + * invalid value reference. Can be modified even if the variable + * does not exist. + * @return 1 if the variable exists, 0 if not + */ +typedef int (*NCDObject_func_getvar) (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +/** + * Callback function for object resolution requests. + * + * @param obj const pointer to the NCDObject this is being called for. + * {@link NCDObject_DataPtr} and {@link NCDObject_DataInt} can be + * used to retrieve state information which was passed to + * {@link NCDObject_Build} or {@link NCDObject_BuildFull}. + * @param name name of the object being resolved, in form of an {@link NCDStringIndex} + * string identifier + * @param out_object If the object exists, *out_object should be set to the result + * object. Can be modified even if the object does not exist. + * @return 1 if the object exists, 0 if not + */ +typedef int (*NCDObject_func_getobj) (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); + +struct NCDObject_s { + NCD_string_id_t type; + int data_int; + void *data_ptr; + void *method_user; + NCDObject_func_getvar func_getvar; + NCDObject_func_getobj func_getobj; +}; + +/** + * Basic object construction function. + * This is equivalent to calling {@link NCDObject_BuildFull} with data_int=0 + * and method_user=data_ptr. See that function for detailed documentation. + */ +static NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj); + +/** + * Constructs an {@link NCDObject} structure. + * This is the full version where all supported parameters have to be provided. + * In most cases, {@link NCDObject_Build} will suffice. + * + * @param type type of the object for the purpose of calling method-like statements + * on the object, in form of an {@link NCDStringIndex} string identifier. + * May be set to -1 if the object has no methods. + * @param data_ptr state-keeping pointer which can be restored from callbacks using + * {@link NCDObject_DataPtr} + * @param data_int state-keeping integer which can be restored from callbacks using + * {@link NCDObject_DataInt} + * @param method_user state-keeping pointer to be passed to new method-like statements + * created using this object. The value of this argument will be + * available as params->method_user within the {@link NCDModule_func_new2} + * module backend callback. + * @param func_getvar callback for resolving variables within the object. This must not + * be NULL; if the object exposes no variables, pass {@link NCDObject_no_getvar}. + * @param func_getobj callback for resolving objects within the object. This must not + * be NULL; if the object exposes no objects, pass {@link NCDObject_no_getobj}. + * @return an NCDObject structure encapsulating the information given + */ +static NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj); + +/** + * Returns the 'type' attribute; see {@link NCDObject_BuildFull}. + */ +static NCD_string_id_t NCDObject_Type (const NCDObject *o); + +/** + * Returns the 'data_ptr' attribute; see {@link NCDObject_BuildFull}. + */ +static void * NCDObject_DataPtr (const NCDObject *o); + +/** + * Returns the 'data_int' attribute; see {@link NCDObject_BuildFull}. + */ +static int NCDObject_DataInt (const NCDObject *o); + +/** + * Returns the 'method_user' attribute; see {@link NCDObject_BuildFull}. + */ +static void * NCDObject_MethodUser (const NCDObject *o); + +/** + * Attempts to resolve a variable within the object. + * This just calls {@link NCDObject_func_getvar}, but also has some assertions to detect + * incorrect behavior of the callback. + */ +static int NCDObject_GetVar (const NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +/** + * Attempts to resolve an object within the object. + * This just calls {@link NCDObject_func_getobj}, but also has some assertions to detect + * incorrect behavior of the callback. + */ +static int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Resolves a variable expression starting with this object. + * A variable expression is usually represented in dotted form, + * e.g. object1.object2.variable (for a named variable) or object1.object2.object3 + * (for an empty string variable). This function however receives the expression + * as an array of string identifiers. + * + * Consult the implementation for exact semantics of variable expression resolution. + * + * @param o object to start the resolution with + * @param names pointer to an array of names for the resolution. May be NULL if num_names is 0. + * @param num_names number in names in the array + * @param mem pointer to the memory object where the resulting value + * should be constructed + * @param out_value If the variable exists, *out_value will be set to the value + * reference to the result value. If the variable exists but there + * was an error constructing the value, will be set to an + * invalid value reference. May be modified even if the variable + * does not exist. + * @return 1 if the variable exists, 0 if not + */ +static int NCDObject_ResolveVarExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +/** + * Resolves an object expression starting with this object. + * An object expression is usually represented in dotted form, + * e.g. object1.object2.object3. This function however receives the expression + * as an array of string identifiers. + * + * Consult the implementation for exact semantics of object expression resolution. + * + * @param o object to start the resolution with + * @param names pointer to an array of names for the resolution. May be NULL if num_names is 0. + * @param num_names number in names in the array + * @param out_object If the object exists, *out_object will be set to the result + * object. May be modified even if the object does not exist. + * @return 1 if the object exists, 0 if not + */ +static int NCDObject_ResolveObjExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) WARN_UNUSED; + +/** + * Returns 0. This can be used as a dummy variable resolution callback. + */ +int NCDObject_no_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +/** + * Returns 0. This can be used as a dummy object resolution callback. + */ +int NCDObject_no_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); + +// + +NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj) +{ + ASSERT(type >= -1) + ASSERT(func_getvar) + ASSERT(func_getobj) + + NCDObject obj; + obj.type = type; + obj.data_int = 0; + obj.data_ptr = data_ptr; + obj.method_user = data_ptr; + obj.func_getvar = func_getvar; + obj.func_getobj = func_getobj; + + return obj; +} + +NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj) +{ + ASSERT(type >= -1) + ASSERT(func_getvar) + ASSERT(func_getobj) + + NCDObject obj; + obj.type = type; + obj.data_int = data_int; + obj.data_ptr = data_ptr; + obj.method_user = method_user; + obj.func_getvar = func_getvar; + obj.func_getobj = func_getobj; + + return obj; +} + +NCD_string_id_t NCDObject_Type (const NCDObject *o) +{ + return o->type; +} + +void * NCDObject_DataPtr (const NCDObject *o) +{ + return o->data_ptr; +} + +int NCDObject_DataInt (const NCDObject *o) +{ + return o->data_int; +} + +void * NCDObject_MethodUser (const NCDObject *o) +{ + return o->method_user; +} + +int NCDObject_GetVar (const NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(name >= 0) + ASSERT(mem) + ASSERT(out_value) + + int res = o->func_getvar(o, name, mem, out_value); + + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(name >= 0) + ASSERT(out_object) + + int res = o->func_getobj(o, name, out_object); + + ASSERT(res == 0 || res == 1) + + return res; +} + +static NCDObject NCDObject__dig_into_object (NCDObject object) +{ + NCDObject obj2; + while (NCDObject_GetObj(&object, NCD_STRING_EMPTY, &obj2)) { + object = obj2; + } + + return object; +} + +int NCDObject_ResolveVarExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(num_names == 0 || names) + ASSERT(mem) + ASSERT(out_value) + + NCDObject object = NCDObject__dig_into_object(*o); + + while (num_names > 0) { + NCDObject obj2; + if (!NCDObject_GetObj(&object, *names, &obj2)) { + if (num_names == 1 && NCDObject_GetVar(&object, *names, mem, out_value)) { + return 1; + } + + return 0; + } + + object = NCDObject__dig_into_object(obj2); + + names++; + num_names--; + } + + return NCDObject_GetVar(&object, NCD_STRING_EMPTY, mem, out_value); +} + +int NCDObject_ResolveObjExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) +{ + ASSERT(num_names == 0 || names) + ASSERT(out_object) + + NCDObject object = NCDObject__dig_into_object(*o); + + while (num_names > 0) { + NCDObject obj2; + if (!NCDObject_GetObj(&object, *names, &obj2)) { + return 0; + } + + object = NCDObject__dig_into_object(obj2); + + names++; + num_names--; + } + + *out_object = object; + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.c b/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.c new file mode 100644 index 0000000..906509d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.c @@ -0,0 +1,127 @@ +/** + * @file NCDPlaceholderDb.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "NCDPlaceholderDb.h" + +#include + +int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + o->count = 0; + o->capacity = 1; + o->string_index = string_index; + + if (!(o->arr = BAllocArray(o->capacity, sizeof(o->arr[0])))) { + BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed"); + return 0; + } + + return 1; +} + +void NCDPlaceholderDb_Free (NCDPlaceholderDb *o) +{ + for (size_t i = 0; i < o->count; i++) { + BFree(o->arr[i].varnames); + } + + BFree(o->arr); +} + +int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int *out_plid) +{ + ASSERT(varname) + ASSERT(out_plid) + ASSERT(o->count <= o->capacity) + ASSERT(o->capacity > 0) + + if (o->count == o->capacity) { + if (o->capacity > SIZE_MAX / 2) { + BLog(BLOG_ERROR, "too many placeholder entries (cannot resize)"); + return 0; + } + size_t newcap = 2 * o->capacity; + + struct NCDPlaceholderDb__entry *newarr = BAllocArray(newcap, sizeof(newarr[0])); + if (!newarr) { + BLog(BLOG_ERROR, "BAllocArray failed"); + return 0; + } + + memcpy(newarr, o->arr, o->count * sizeof(newarr[0])); + BFree(o->arr); + + o->arr = newarr; + o->capacity = newcap; + } + + ASSERT(o->count < o->capacity) + + if (o->count > INT_MAX) { + BLog(BLOG_ERROR, "too many placeholder entries (cannot fit integer)"); + return 0; + } + + NCD_string_id_t *varnames; + size_t num_names; + if (!ncd_make_name_indices(o->string_index, varname, &varnames, &num_names)) { + BLog(BLOG_ERROR, "ncd_make_name_indices failed"); + return 0; + } + + *out_plid = o->count; + + o->arr[o->count].varnames = varnames; + o->arr[o->count].num_names = num_names; + o->count++; + + return 1; +} + +void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names) +{ + ASSERT(plid >= 0) + ASSERT(plid < o->count) + ASSERT(out_varnames) + ASSERT(out_num_names) + + *out_varnames = o->arr[plid].varnames; + *out_num_names = o->arr[plid].num_names; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.h b/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.h new file mode 100644 index 0000000..d6a1f40 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDPlaceholderDb.h @@ -0,0 +1,95 @@ +/** + * @file NCDPlaceholderDb.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDPLACEHOLDERDB_H +#define BADVPN_NCDPLACEHOLDERDB_H + +#include + +#include +#include + +struct NCDPlaceholderDb__entry { + NCD_string_id_t *varnames; + size_t num_names; +}; + +/** + * Associates variable placeholder numbers to variable names. + * This is populated by {@link NCDInterpProcess_Init} when converting the {@link NCDValue} + * objects in the AST to compact representations in {@link NCDValMem}. Variables are + * replaced with placeholder identifiers (integers), which this object associates + * with their names. + * During interpretation, when a statement is being initialized, the compact form held + * by {@link NCDInterpProcess} is byte-copied, and placeholders are replaced with the + * values of corresponding variables using {@link NCDVal_ReplacePlaceholders}. + */ +typedef struct { + struct NCDPlaceholderDb__entry *arr; + size_t count; + size_t capacity; + NCDStringIndex *string_index; +} NCDPlaceholderDb; + +/** + * Initializes the placeholder database. + * Returns 1 on success, and 0 on failure. + */ +int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index) WARN_UNUSED; + +/** + * Frees the placeholder database. + */ +void NCDPlaceholderDb_Free (NCDPlaceholderDb *o); + +/** + * Adds a variable to the database. + * + * @param varname name of the variable (text, including dots). Must not be NULL. + * @param out_plid on success, the placeholder identifier will be returned here. Must + * not be NULL. + * @return 1 on success, 0 on failure + */ +int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int *out_plid) WARN_UNUSED; + +/** + * Retrieves the name of the variable associated with a placeholder identifier. + * + * @param plid placeholder identifier; must have been previously provided by + * {@link NCDPlaceholderDb_AddVariable}. + * @return name of the variable, split by dots. The returned value points to + * an array of pointers to strings, which is terminated by a NULL + * pointer. These all point to internal data in the placeholder + * database; they must not be modified, and remain valid until the + * database is freed. + * Note that there will always be at least one string in the result. + */ +void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDStringIndex.c b/app/src/main/jni/badvpn/ncd/NCDStringIndex.c new file mode 100644 index 0000000..87a231d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDStringIndex.c @@ -0,0 +1,262 @@ +/** + * @file NCDStringIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "NCDStringIndex.h" + +#include "NCDStringIndex_hash.h" +#include + +#define GROWARRAY_NAME Array +#define GROWARRAY_OBJECT_TYPE NCDStringIndex +#define GROWARRAY_ARRAY_MEMBER entries +#define GROWARRAY_CAPACITY_MEMBER entries_capacity +#define GROWARRAY_MAX_CAPACITY NCD_STRING_ID_MAX +#include + +#include + +// NOTE: keep synchronized with static_strings.h +static const char *static_strings[] = { + "", + "_args", + "_arg0", + "_arg1", + "_arg2", + "_arg3", + "_arg4", + "_arg5", + "_arg6", + "_arg7", + "_arg8", + "_arg9", + "_arg10", + "_arg11", + "_arg12", + "_arg13", + "_arg14", + "_arg15", + "_arg16", + "_arg17", + "_arg18", + "_arg19", + "true", + "false", + "", + "_caller", + "succeeded", + "is_error", + "not_eof", + "length", + "type", + "exit_status", + "size" +}; + +static NCD_string_id_t do_get (NCDStringIndex *o, const char *str, size_t str_len) +{ + ASSERT(str) + + NCDStringIndex_hash_key key = {str, str_len}; + NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, key); + ASSERT(ref.link == -1 || ref.link >= 0) + ASSERT(ref.link == -1 || ref.link < o->entries_size) + ASSERT(ref.link == -1 || (ref.ptr->str_len == str_len && !memcmp(ref.ptr->str, str, str_len))) + + if (ref.link != -1) { + return ref.link; + } + + if (o->entries_size == o->entries_capacity) { + if (!Array_DoubleUp(o)) { + BLog(BLOG_ERROR, "Array_DoubleUp failed"); + return -1; + } + + if (!NCDStringIndex__Hash_MultiplyBuckets(&o->hash, o->entries, 1)) { + BLog(BLOG_ERROR, "NCDStringIndex__Hash_MultiplyBuckets failed"); + return -1; + } + } + + ASSERT(o->entries_size < o->entries_capacity) + + struct NCDStringIndex__entry *entry = &o->entries[o->entries_size]; + + if (!(entry->str = b_strdup_bin(str, str_len))) { + BLog(BLOG_ERROR, "b_strdup_bin failed"); + return -1; + } + entry->str_len = str_len; + entry->has_nulls = !!memchr(str, '\0', str_len); + + NCDStringIndex__HashRef newref = {entry, o->entries_size}; + int res = NCDStringIndex__Hash_Insert(&o->hash, o->entries, newref, NULL); + ASSERT_EXECUTE(res) + + return o->entries_size++; +} + +int NCDStringIndex_Init (NCDStringIndex *o) +{ + o->entries_size = 0; + + if (!Array_Init(o, NCDSTRINGINDEX_INITIAL_CAPACITY)) { + BLog(BLOG_ERROR, "Array_Init failed"); + goto fail0; + } + + if (!NCDStringIndex__Hash_Init(&o->hash, NCDSTRINGINDEX_INITIAL_HASH_BUCKETS)) { + BLog(BLOG_ERROR, "NCDStringIndex__Hash_Init failed"); + goto fail1; + } + + for (size_t i = 0; i < B_ARRAY_LENGTH(static_strings); i++) { + if (do_get(o, static_strings[i], strlen(static_strings[i])) < 0) { + goto fail2; + } + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + for (NCD_string_id_t i = 0; i < o->entries_size; i++) { + free(o->entries[i].str); + } + NCDStringIndex__Hash_Free(&o->hash); +fail1: + Array_Free(o); +fail0: + return 0; +} + +void NCDStringIndex_Free (NCDStringIndex *o) +{ + DebugObject_Free(&o->d_obj); + + for (NCD_string_id_t i = 0; i < o->entries_size; i++) { + free(o->entries[i].str); + } + + NCDStringIndex__Hash_Free(&o->hash); + Array_Free(o); +} + +NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return NCDStringIndex_LookupBin(o, str, strlen(str)); +} + +NCD_string_id_t NCDStringIndex_LookupBin (NCDStringIndex *o, const char *str, size_t str_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + NCDStringIndex_hash_key key = {str, str_len}; + NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, key); + ASSERT(ref.link == -1 || ref.link >= 0) + ASSERT(ref.link == -1 || ref.link < o->entries_size) + ASSERT(ref.link == -1 || (ref.ptr->str_len == str_len && !memcmp(ref.ptr->str, str, str_len))) + + return ref.link; +} + +NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return NCDStringIndex_GetBin(o, str, strlen(str)); +} + +NCD_string_id_t NCDStringIndex_GetBin (NCDStringIndex *o, const char *str, size_t str_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return do_get(o, str, str_len); +} + +const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].str; +} + +size_t NCDStringIndex_Length (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].str_len; +} + +int NCDStringIndex_HasNulls (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].has_nulls; +} + +int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests) +{ + DebugObject_Access(&o->d_obj); + ASSERT(requests) + + while (requests->str) { + NCD_string_id_t id = NCDStringIndex_Get(o, requests->str); + if (id < 0) { + return 0; + } + requests->id = id; + requests++; + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDStringIndex.h b/app/src/main/jni/badvpn/ncd/NCDStringIndex.h new file mode 100644 index 0000000..78e20ec --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDStringIndex.h @@ -0,0 +1,83 @@ +/** + * @file NCDStringIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_STRING_INDEX_H +#define BADVPN_NCD_STRING_INDEX_H + +#include + +#include +#include +#include + +#define NCDSTRINGINDEX_INITIAL_CAPACITY 256 +#define NCDSTRINGINDEX_INITIAL_HASH_BUCKETS 256 + +typedef int NCD_string_id_t; + +#define NCD_STRING_ID_MAX INT_MAX + +struct NCDStringIndex__entry { + char *str; + size_t str_len; + int has_nulls; + NCD_string_id_t hash_next; +}; + +typedef struct { const char *str; size_t len; } NCDStringIndex_hash_key; +typedef struct NCDStringIndex__entry *NCDStringIndex_hash_arg; + +#include "NCDStringIndex_hash.h" +#include + +typedef struct { + struct NCDStringIndex__entry *entries; + NCD_string_id_t entries_capacity; + NCD_string_id_t entries_size; + NCDStringIndex__Hash hash; + DebugObject d_obj; +} NCDStringIndex; + +struct NCD_string_request { + const char *str; + NCD_string_id_t id; +}; + +int NCDStringIndex_Init (NCDStringIndex *o) WARN_UNUSED; +void NCDStringIndex_Free (NCDStringIndex *o); +NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str); +NCD_string_id_t NCDStringIndex_LookupBin (NCDStringIndex *o, const char *str, size_t str_len); +NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str); +NCD_string_id_t NCDStringIndex_GetBin (NCDStringIndex *o, const char *str, size_t str_len); +const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id); +size_t NCDStringIndex_Length (NCDStringIndex *o, NCD_string_id_t id); +int NCDStringIndex_HasNulls (NCDStringIndex *o, NCD_string_id_t id); +int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDStringIndex_hash.h b/app/src/main/jni/badvpn/ncd/NCDStringIndex_hash.h new file mode 100644 index 0000000..9a751fb --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDStringIndex_hash.h @@ -0,0 +1,13 @@ +#define CHASH_PARAM_NAME NCDStringIndex__Hash +#define CHASH_PARAM_ENTRY struct NCDStringIndex__entry +#define CHASH_PARAM_LINK NCD_string_id_t +#define CHASH_PARAM_KEY NCDStringIndex_hash_key +#define CHASH_PARAM_ARG NCDStringIndex_hash_arg +#define CHASH_PARAM_NULL ((NCD_string_id_t)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) badvpn_djb2_hash_bin((const uint8_t *)(entry).ptr->str, (entry).ptr->str_len) +#define CHASH_PARAM_KEYHASH(arg, key) badvpn_djb2_hash_bin((const uint8_t *)(key).str, (key).len) +#define CHASH_PARAM_ENTRYHASH_IS_CHEAP 0 +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) ((entry1).ptr->str_len == (entry2).ptr->str_len && !memcmp((entry1).ptr->str, (entry2).ptr->str, (entry1).ptr->str_len)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) ((key1).len == (entry2).ptr->str_len && !memcmp((key1).str, (entry2).ptr->str, (key1).len)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/app/src/main/jni/badvpn/ncd/NCDSugar.c b/app/src/main/jni/badvpn/ncd/NCDSugar.c new file mode 100644 index 0000000..669bec8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDSugar.c @@ -0,0 +1,253 @@ +/** + * @file NCDSugar.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "NCDSugar.h" + +struct desugar_state { + NCDProgram *prog; + size_t template_name_ctr; +}; + +static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val); +static int desugar_block (struct desugar_state *state, NCDBlock *block); +static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next); +static int desugar_foreach (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next); + +static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val) +{ + char name[40]; + snprintf(name, sizeof(name), "__tmpl%zu", state->template_name_ctr); + state->template_name_ctr++; + + if (!desugar_block(state, &block)) { + NCDBlock_Free(&block); + return 0; + } + + NCDProcess proc_tmp; + if (!NCDProcess_Init(&proc_tmp, 1, name, block)) { + NCDBlock_Free(&block); + return 0; + } + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc_tmp); + + if (!NCDProgram_PrependElem(state->prog, elem)) { + NCDProgramElem_Free(&elem); + return 0; + } + + if (!NCDValue_InitString(out_name_val, name)) { + return 0; + } + + return 1; +} + +static int desugar_block (struct desugar_state *state, NCDBlock *block) +{ + NCDStatement *stmt = NCDBlock_FirstStatement(block); + + while (stmt) { + switch (NCDStatement_Type(stmt)) { + case NCDSTATEMENT_REG: { + stmt = NCDBlock_NextStatement(block, stmt); + } break; + + case NCDSTATEMENT_IF: { + if (!desugar_if(state, block, stmt, &stmt)) { + return 0; + } + } break; + + case NCDSTATEMENT_FOREACH: { + if (!desugar_foreach(state, block, stmt, &stmt)) { + return 0; + } + } break; + + default: ASSERT(0); + } + } + + return 1; +} + +static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next) +{ + ASSERT(NCDStatement_Type(stmt) == NCDSTATEMENT_IF) + + NCDValue args; + NCDValue_InitList(&args); + + NCDIfBlock *ifblock = NCDStatement_IfBlock(stmt); + + while (NCDIfBlock_FirstIf(ifblock)) { + NCDIf ifc = NCDIfBlock_GrabIf(ifblock, NCDIfBlock_FirstIf(ifblock)); + + NCDValue if_cond; + NCDBlock if_block; + NCDIf_FreeGrab(&ifc, &if_cond, &if_block); + + if (!NCDValue_ListAppend(&args, if_cond)) { + NCDValue_Free(&if_cond); + NCDBlock_Free(&if_block); + goto fail; + } + + NCDValue action_arg; + if (!add_template(state, if_block, &action_arg)) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, action_arg)) { + NCDValue_Free(&action_arg); + goto fail; + } + } + + if (NCDStatement_IfElse(stmt)) { + NCDBlock else_block = NCDStatement_IfGrabElse(stmt); + + NCDValue action_arg; + if (!add_template(state, else_block, &action_arg)) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, action_arg)) { + NCDValue_Free(&action_arg); + goto fail; + } + } + + NCDStatement new_stmt; + if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "embcall2_multif", args)) { + goto fail; + } + + stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt); + + *out_next = NCDBlock_NextStatement(block, stmt); + return 1; + +fail: + NCDValue_Free(&args); + return 0; +} + +static int desugar_foreach (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next) +{ + ASSERT(NCDStatement_Type(stmt) == NCDSTATEMENT_FOREACH) + + NCDValue args; + NCDValue_InitList(&args); + + NCDValue collection; + NCDBlock foreach_block; + NCDStatement_ForeachGrab(stmt, &collection, &foreach_block); + + NCDValue template_arg; + if (!add_template(state, foreach_block, &template_arg)) { + NCDValue_Free(&collection); + goto fail; + } + + if (!NCDValue_ListAppend(&args, collection)) { + NCDValue_Free(&template_arg); + NCDValue_Free(&collection); + goto fail; + } + + if (!NCDValue_ListAppend(&args, template_arg)) { + NCDValue_Free(&template_arg); + goto fail; + } + + NCDValue name1_arg; + if (!NCDValue_InitString(&name1_arg, NCDStatement_ForeachName1(stmt))) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, name1_arg)) { + NCDValue_Free(&name1_arg); + goto fail; + } + + if (NCDStatement_ForeachName2(stmt)) { + NCDValue name2_arg; + if (!NCDValue_InitString(&name2_arg, NCDStatement_ForeachName2(stmt))) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, name2_arg)) { + NCDValue_Free(&name2_arg); + goto fail; + } + } + + NCDStatement new_stmt; + if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "foreach_emb", args)) { + goto fail; + } + + stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt); + + *out_next = NCDBlock_NextStatement(block, stmt); + return 1; + +fail: + NCDValue_Free(&args); + return 0; +} + +int NCDSugar_Desugar (NCDProgram *prog) +{ + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE)) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE_GUARD)) + + struct desugar_state state; + state.prog = prog; + state.template_name_ctr = 0; + + for (NCDProgramElem *elem = NCDProgram_FirstElem(prog); elem; elem = NCDProgram_NextElem(prog, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *proc = NCDProgramElem_Process(elem); + + if (!desugar_block(&state, NCDProcess_Block(proc))) { + return 0; + } + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDSugar.h b/app/src/main/jni/badvpn/ncd/NCDSugar.h new file mode 100644 index 0000000..0aa0ad2 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDSugar.h @@ -0,0 +1,38 @@ +/** + * @file NCDSugar.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDSUGAR_H +#define BADVPN_NCDSUGAR_H + +#include +#include + +int NCDSugar_Desugar (NCDProgram *prog) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDVal.c b/app/src/main/jni/badvpn/ncd/NCDVal.c new file mode 100644 index 0000000..da605bc --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDVal.c @@ -0,0 +1,2065 @@ +/** + * @file NCDVal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "NCDVal.h" + +#include + +#define TYPE_MASK_EXTERNAL_TYPE ((1 << 3) - 1) +#define TYPE_MASK_INTERNAL_TYPE ((1 << 5) - 1) +#define TYPE_SHIFT_DEPTH 5 + +#define STOREDSTRING_TYPE (NCDVAL_STRING | (0 << 3)) +#define IDSTRING_TYPE (NCDVAL_STRING | (1 << 3)) +#define EXTERNALSTRING_TYPE (NCDVAL_STRING | (2 << 3)) +#define COMPOSEDSTRING_TYPE (NCDVAL_STRING | (3 << 3)) + +static int make_type (int internal_type, int depth) +{ + ASSERT(internal_type == NCDVAL_LIST || + internal_type == NCDVAL_MAP || + internal_type == STOREDSTRING_TYPE || + internal_type == IDSTRING_TYPE || + internal_type == EXTERNALSTRING_TYPE || + internal_type == COMPOSEDSTRING_TYPE) + ASSERT(depth >= 0) + ASSERT(depth <= NCDVAL_MAX_DEPTH) + + return (internal_type | (depth << TYPE_SHIFT_DEPTH)); +} + +static int get_external_type (int type) +{ + return (type & TYPE_MASK_EXTERNAL_TYPE); +} + +static int get_internal_type (int type) +{ + return (type & TYPE_MASK_INTERNAL_TYPE); +} + +static int get_depth (int type) +{ + return (type >> TYPE_SHIFT_DEPTH); +} + +static int bump_depth (int *type_ptr, int elem_depth) +{ + if (get_depth(*type_ptr) < elem_depth + 1) { + if (elem_depth + 1 > NCDVAL_MAX_DEPTH) { + return 0; + } + *type_ptr = make_type(get_internal_type(*type_ptr), elem_depth + 1); + } + + return 1; +} + +static void * NCDValMem__BufAt (NCDValMem *o, NCDVal__idx idx) +{ + ASSERT(idx >= 0) + ASSERT(idx < o->used) + + return (o->buf ? o->buf : o->fastbuf) + idx; +} + +static NCDVal__idx NCDValMem__Alloc (NCDValMem *o, NCDVal__idx alloc_size, NCDVal__idx align) +{ + NCDVal__idx mod = o->used % align; + NCDVal__idx align_extra = mod ? (align - mod) : 0; + + if (alloc_size > NCDVAL_MAXIDX - align_extra) { + return -1; + } + NCDVal__idx aligned_alloc_size = align_extra + alloc_size; + + if (aligned_alloc_size > o->size - o->used) { + NCDVal__idx newsize = (o->buf ? o->size : NCDVAL_FIRST_SIZE); + while (aligned_alloc_size > newsize - o->used) { + if (newsize > NCDVAL_MAXIDX / 2) { + return -1; + } + newsize *= 2; + } + + char *newbuf; + + if (!o->buf) { + newbuf = malloc(newsize); + if (!newbuf) { + return -1; + } + memcpy(newbuf, o->fastbuf, o->used); + } else { + newbuf = realloc(o->buf, newsize); + if (!newbuf) { + return -1; + } + } + + o->buf = newbuf; + o->size = newsize; + } + + NCDVal__idx idx = o->used + align_extra; + o->used += aligned_alloc_size; + + return idx; +} + +static NCDValRef NCDVal__Ref (NCDValMem *mem, NCDVal__idx idx) +{ + ASSERT(idx == -1 || mem) + + NCDValRef ref = {mem, idx}; + return ref; +} + +static void NCDVal__AssertMem (NCDValMem *mem) +{ + ASSERT(mem) + ASSERT(mem->size >= 0) + ASSERT(mem->used >= 0) + ASSERT(mem->used <= mem->size) + ASSERT(mem->buf || mem->size == NCDVAL_FASTBUF_SIZE) + ASSERT(!mem->buf || mem->size >= NCDVAL_FIRST_SIZE) +} + +static void NCDVal_AssertExternal (NCDValMem *mem, const void *e_buf, size_t e_len) +{ +#ifndef NDEBUG + const char *e_cbuf = e_buf; + char *buf = (mem->buf ? mem->buf : mem->fastbuf); + ASSERT(e_cbuf >= buf + mem->size || e_cbuf + e_len <= buf) +#endif +} + +static void NCDVal__AssertValOnly (NCDValMem *mem, NCDVal__idx idx) +{ + // placeholders + if (idx < -1) { + return; + } + + ASSERT(idx >= 0) + ASSERT(idx + sizeof(int) <= mem->used) + +#ifndef NDEBUG + int *type_ptr = NCDValMem__BufAt(mem, idx); + + ASSERT(get_depth(*type_ptr) >= 0) + ASSERT(get_depth(*type_ptr) <= NCDVAL_MAX_DEPTH) + + switch (get_internal_type(*type_ptr)) { + case STOREDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__string) <= mem->used) + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + ASSERT(str_e->length >= 0) + ASSERT(idx + sizeof(struct NCDVal__string) + str_e->length + 1 <= mem->used) + } break; + case NCDVAL_LIST: { + ASSERT(idx + sizeof(struct NCDVal__list) <= mem->used) + struct NCDVal__list *list_e = NCDValMem__BufAt(mem, idx); + ASSERT(list_e->maxcount >= 0) + ASSERT(list_e->count >= 0) + ASSERT(list_e->count <= list_e->maxcount) + ASSERT(idx + sizeof(struct NCDVal__list) + list_e->maxcount * sizeof(NCDVal__idx) <= mem->used) + } break; + case NCDVAL_MAP: { + ASSERT(idx + sizeof(struct NCDVal__map) <= mem->used) + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, idx); + ASSERT(map_e->maxcount >= 0) + ASSERT(map_e->count >= 0) + ASSERT(map_e->count <= map_e->maxcount) + ASSERT(idx + sizeof(struct NCDVal__map) + map_e->maxcount * sizeof(struct NCDVal__mapelem) <= mem->used) + } break; + case IDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__idstring) <= mem->used) + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(mem, idx); + ASSERT(ids_e->string_id >= 0) + ASSERT(ids_e->string_index) + } break; + case EXTERNALSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__externalstring) <= mem->used) + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx); + ASSERT(exs_e->data) + ASSERT(!exs_e->ref.target || exs_e->ref.next >= -1) + ASSERT(!exs_e->ref.target || exs_e->ref.next < mem->used) + } break; + case COMPOSEDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__composedstring) <= mem->used) + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(mem, idx); + ASSERT(cms_e->func_getptr) + ASSERT(!cms_e->ref.target || cms_e->ref.next >= -1) + ASSERT(!cms_e->ref.target || cms_e->ref.next < mem->used) + } break; + default: ASSERT(0); + } +#endif +} + +static void NCDVal__AssertVal (NCDValRef val) +{ + NCDVal__AssertMem(val.mem); + NCDVal__AssertValOnly(val.mem, val.idx); +} + +static NCDValMapElem NCDVal__MapElem (NCDVal__idx elemidx) +{ + ASSERT(elemidx >= 0 || elemidx == -1) + + NCDValMapElem me = {elemidx}; + return me; +} + +static void NCDVal__MapAssertElemOnly (NCDValRef map, NCDVal__idx elemidx) +{ +#ifndef NDEBUG + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + ASSERT(elemidx >= map.idx + offsetof(struct NCDVal__map, elems)) + ASSERT(elemidx < map.idx + offsetof(struct NCDVal__map, elems) + map_e->count * sizeof(struct NCDVal__mapelem)) + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, elemidx); + NCDVal__AssertValOnly(map.mem, me_e->key_idx); + NCDVal__AssertValOnly(map.mem, me_e->val_idx); +#endif +} + +static void NCDVal__MapAssertElem (NCDValRef map, NCDValMapElem me) +{ + ASSERT(NCDVal_IsMap(map)) + NCDVal__MapAssertElemOnly(map, me.elemidx); +} + +static NCDVal__idx NCDVal__MapElemIdx (NCDVal__idx mapidx, NCDVal__idx pos) +{ + return mapidx + offsetof(struct NCDVal__map, elems) + pos * sizeof(struct NCDVal__mapelem); +} + +static int NCDVal__Depth (NCDValRef val) +{ + ASSERT(val.idx != -1) + + // handle placeholders + if (val.idx < 0) { + return 0; + } + + int *elem_type_ptr = NCDValMem__BufAt(val.mem, val.idx); + int depth = get_depth(*elem_type_ptr); + ASSERT(depth >= 0) + ASSERT(depth <= NCDVAL_MAX_DEPTH) + + return depth; +} + +static int NCDValMem__NeedRegisterLink (NCDValMem *mem, NCDVal__idx val_idx) +{ + NCDVal__AssertValOnly(mem, val_idx); + + return !(val_idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(mem, val_idx)) == COMPOSEDSTRING_TYPE; +} + +static int NCDValMem__RegisterLink (NCDValMem *mem, NCDVal__idx val_idx, NCDVal__idx link_idx) +{ + NCDVal__AssertValOnly(mem, val_idx); + ASSERT(NCDValMem__NeedRegisterLink(mem, val_idx)) + + NCDVal__idx cms_link_idx = NCDValMem__Alloc(mem, sizeof(struct NCDVal__cms_link), __alignof(struct NCDVal__cms_link)); + if (cms_link_idx < 0) { + return 0; + } + + struct NCDVal__cms_link *cms_link = NCDValMem__BufAt(mem, cms_link_idx); + cms_link->link_idx = link_idx; + cms_link->next_cms_link = mem->first_cms_link; + mem->first_cms_link = cms_link_idx; + + return 1; +} + +static void NCDValMem__PopLastRegisteredLink (NCDValMem *mem) +{ + ASSERT(mem->first_cms_link != -1) + + struct NCDVal__cms_link *cms_link = NCDValMem__BufAt(mem, mem->first_cms_link); + mem->first_cms_link = cms_link->next_cms_link; +} + +static NCDValRef NCDVal__CopyComposedStringToStored (NCDValRef val) +{ + ASSERT(NCDVal_IsComposedString(val)) + + struct NCDVal__composedstring cms_e = *(struct NCDVal__composedstring *)NCDValMem__BufAt(val.mem, val.idx); + + NCDValRef copy = NCDVal_NewStringUninitialized(val.mem, cms_e.length); + if (NCDVal_IsInvalid(copy)) { + return NCDVal_NewInvalid(); + } + + char *copy_data = (char *)NCDVal_StringData(copy); + + size_t pos = 0; + while (pos < cms_e.length) { + const char *chunk_data; + size_t chunk_len; + cms_e.func_getptr(cms_e.user, cms_e.offset + pos, &chunk_data, &chunk_len); + ASSERT(chunk_data) + ASSERT(chunk_len > 0) + if (chunk_len > cms_e.length - pos) { + chunk_len = cms_e.length - pos; + } + memcpy(copy_data + pos, chunk_data, chunk_len); + pos += chunk_len; + } + + return copy; +} + +static const char * NCDVal__composedstring_cstring_func (const b_cstring *cstr, size_t offset, size_t *out_length) +{ + ASSERT(offset < cstr->length) + ASSERT(out_length) + ASSERT(cstr->func == NCDVal__composedstring_cstring_func) + + size_t str_offset = cstr->user1.size; + NCDVal_ComposedString_func_getptr func_getptr = (NCDVal_ComposedString_func_getptr)cstr->user2.fptr; + void *user = cstr->user3.ptr; + + const char *data; + func_getptr(user, str_offset + offset, &data, out_length); + + ASSERT(data) + ASSERT(*out_length > 0) + + return data; +} + +#include "NCDVal_maptree.h" +#include + +void NCDValMem_Init (NCDValMem *o) +{ + o->buf = NULL; + o->size = NCDVAL_FASTBUF_SIZE; + o->used = 0; + o->first_ref = -1; + o->first_cms_link = -1; +} + +void NCDValMem_Free (NCDValMem *o) +{ + NCDVal__AssertMem(o); + + NCDVal__idx refidx = o->first_ref; + while (refidx != -1) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx); + ASSERT(ref->target) + BRefTarget_Deref(ref->target); + refidx = ref->next; + } + + if (o->buf) { + BFree(o->buf); + } +} + +int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other) +{ + NCDVal__AssertMem(other); + + o->size = other->size; + o->used = other->used; + o->first_ref = other->first_ref; + o->first_cms_link = other->first_cms_link; + + if (!other->buf) { + o->buf = NULL; + memcpy(o->fastbuf, other->fastbuf, other->used); + } else { + o->buf = BAlloc(other->size); + if (!o->buf) { + goto fail0; + } + memcpy(o->buf, other->buf, other->used); + } + + NCDVal__idx refidx = o->first_ref; + while (refidx != -1) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx); + ASSERT(ref->target) + if (!BRefTarget_Ref(ref->target)) { + goto fail1; + } + refidx = ref->next; + } + + return 1; + +fail1:; + NCDVal__idx undo_refidx = o->first_ref; + while (undo_refidx != refidx) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, undo_refidx); + BRefTarget_Deref(ref->target); + undo_refidx = ref->next; + } + if (o->buf) { + BFree(o->buf); + } +fail0: + return 0; +} + +int NCDValMem_ConvertNonContinuousStrings (NCDValMem *o, NCDValRef *root_val) +{ + NCDVal__AssertMem(o); + ASSERT(root_val) + ASSERT(root_val->mem == o) + NCDVal__AssertValOnly(o, root_val->idx); + + while (o->first_cms_link != -1) { + struct NCDVal__cms_link cms_link = *(struct NCDVal__cms_link *)NCDValMem__BufAt(o, o->first_cms_link); + + NCDVal__idx val_idx = *(NCDVal__idx *)NCDValMem__BufAt(o, cms_link.link_idx); + NCDValRef val = NCDVal__Ref(o, val_idx); + ASSERT(NCDVal_IsComposedString(val)) + + NCDValRef copy = NCDVal__CopyComposedStringToStored(val); + if (NCDVal_IsInvalid(copy)) { + return 0; + } + + *(int *)NCDValMem__BufAt(o, cms_link.link_idx) = copy.idx; + + o->first_cms_link = cms_link.next_cms_link; + } + + if (NCDVal_IsComposedString(*root_val)) { + NCDValRef copy = NCDVal__CopyComposedStringToStored(*root_val); + if (NCDVal_IsInvalid(copy)) { + return 0; + } + *root_val = copy; + } + + return 1; +} + +void NCDVal_Assert (NCDValRef val) +{ + ASSERT(val.idx == -1 || (NCDVal__AssertVal(val), 1)) +} + +int NCDVal_IsInvalid (NCDValRef val) +{ + NCDVal_Assert(val); + + return (val.idx == -1); +} + +int NCDVal_IsPlaceholder (NCDValRef val) +{ + NCDVal_Assert(val); + + return (val.idx < -1); +} + +int NCDVal_Type (NCDValRef val) +{ + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return NCDVAL_PLACEHOLDER; + } + + int *type_ptr = NCDValMem__BufAt(val.mem, val.idx); + + return get_external_type(*type_ptr); +} + +NCDValRef NCDVal_NewInvalid (void) +{ + NCDValRef ref = {NULL, -1}; + return ref; +} + +NCDValRef NCDVal_NewPlaceholder (NCDValMem *mem, int plid) +{ + NCDVal__AssertMem(mem); + ASSERT(plid >= 0) + ASSERT(NCDVAL_MINIDX + plid < -1) + + NCDValRef ref = {mem, NCDVAL_MINIDX + plid}; + return ref; +} + +int NCDVal_PlaceholderId (NCDValRef val) +{ + ASSERT(NCDVal_IsPlaceholder(val)) + + return (val.idx - NCDVAL_MINIDX); +} + +NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val) +{ + NCDVal__AssertMem(mem); + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return NCDVal_NewPlaceholder(mem, NCDVal_PlaceholderId(val)); + } + + void *ptr = NCDValMem__BufAt(val.mem, val.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + + NCDVal__idx size = sizeof(struct NCDVal__string) + str_e->length + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + str_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__string *new_str_e = NCDValMem__BufAt(mem, idx); + + memcpy(new_str_e, str_e, size); + + return NCDVal__Ref(mem, idx); + } break; + + case NCDVAL_LIST: { + struct NCDVal__list *list_e = ptr; + + NCDVal__idx size = sizeof(struct NCDVal__list) + list_e->maxcount * sizeof(NCDVal__idx); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__list)); + if (idx < 0) { + goto fail; + } + + list_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__list *new_list_e = NCDValMem__BufAt(mem, idx); + + *new_list_e = *list_e; + + NCDVal__idx count = list_e->count; + + for (NCDVal__idx i = 0; i < count; i++) { + NCDValRef elem_copy = NCDVal_NewCopy(mem, NCDVal__Ref(val.mem, list_e->elem_indices[i])); + if (NCDVal_IsInvalid(elem_copy)) { + goto fail; + } + + if (NCDValMem__NeedRegisterLink(mem, elem_copy.idx)) { + if (!NCDValMem__RegisterLink(mem, elem_copy.idx, idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx))) { + goto fail; + } + } + + list_e = NCDValMem__BufAt(val.mem, val.idx); + new_list_e = NCDValMem__BufAt(mem, idx); + + new_list_e->elem_indices[i] = elem_copy.idx; + } + + return NCDVal__Ref(mem, idx); + } break; + + case NCDVAL_MAP: { + size_t count = NCDVal_MapCount(val); + + NCDValRef copy = NCDVal_NewMap(mem, count); + if (NCDVal_IsInvalid(copy)) { + goto fail; + } + + for (NCDValMapElem e = NCDVal_MapFirst(val); !NCDVal_MapElemInvalid(e); e = NCDVal_MapNext(val, e)) { + NCDValRef key_copy = NCDVal_NewCopy(mem, NCDVal_MapElemKey(val, e)); + NCDValRef val_copy = NCDVal_NewCopy(mem, NCDVal_MapElemVal(val, e)); + if (NCDVal_IsInvalid(key_copy) || NCDVal_IsInvalid(val_copy)) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(copy, key_copy, val_copy, &inserted)) { + goto fail; + } + ASSERT_EXECUTE(inserted) + } + + return copy; + } break; + + case IDSTRING_TYPE: { + NCDVal__idx size = sizeof(struct NCDVal__idstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__idstring)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__idstring *new_ids_e = NCDValMem__BufAt(mem, idx); + + *new_ids_e = *ids_e; + + return NCDVal__Ref(mem, idx); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + + return NCDVal_NewExternalString(mem, exs_e->data, exs_e->length, exs_e->ref.target); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + + NCDValComposedStringResource resource; + resource.func_getptr = cms_e->func_getptr; + resource.user = cms_e->user; + resource.ref_target = cms_e->ref.target; + + return NCDVal_NewComposedString(mem, resource, cms_e->offset, cms_e->length); + } break; + + default: ASSERT(0); + } + + ASSERT(0); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_Compare (NCDValRef val1, NCDValRef val2) +{ + NCDVal__AssertVal(val1); + NCDVal__AssertVal(val2); + + int type1 = NCDVal_Type(val1); + int type2 = NCDVal_Type(val2); + + if (type1 != type2) { + return (type1 > type2) - (type1 < type2); + } + + switch (type1) { + case NCDVAL_STRING: { + size_t len1 = NCDVal_StringLength(val1); + size_t len2 = NCDVal_StringLength(val2); + size_t min_len = len1 < len2 ? len1 : len2; + + int cmp = NCDVal_StringMemCmp(val1, val2, 0, 0, min_len); + if (cmp) { + return (cmp > 0) - (cmp < 0); + } + + return (len1 > len2) - (len1 < len2); + } break; + + case NCDVAL_LIST: { + size_t count1 = NCDVal_ListCount(val1); + size_t count2 = NCDVal_ListCount(val2); + size_t min_count = count1 < count2 ? count1 : count2; + + for (size_t i = 0; i < min_count; i++) { + NCDValRef ev1 = NCDVal_ListGet(val1, i); + NCDValRef ev2 = NCDVal_ListGet(val2, i); + + int cmp = NCDVal_Compare(ev1, ev2); + if (cmp) { + return cmp; + } + } + + return (count1 > count2) - (count1 < count2); + } break; + + case NCDVAL_MAP: { + NCDValMapElem e1 = NCDVal_MapOrderedFirst(val1); + NCDValMapElem e2 = NCDVal_MapOrderedFirst(val2); + + while (1) { + int inv1 = NCDVal_MapElemInvalid(e1); + int inv2 = NCDVal_MapElemInvalid(e2); + if (inv1 || inv2) { + return inv2 - inv1; + } + + NCDValRef key1 = NCDVal_MapElemKey(val1, e1); + NCDValRef key2 = NCDVal_MapElemKey(val2, e2); + + int cmp = NCDVal_Compare(key1, key2); + if (cmp) { + return cmp; + } + + NCDValRef value1 = NCDVal_MapElemVal(val1, e1); + NCDValRef value2 = NCDVal_MapElemVal(val2, e2); + + cmp = NCDVal_Compare(value1, value2); + if (cmp) { + return cmp; + } + + e1 = NCDVal_MapOrderedNext(val1, e1); + e2 = NCDVal_MapOrderedNext(val2, e2); + } + } break; + + case NCDVAL_PLACEHOLDER: { + int plid1 = NCDVal_PlaceholderId(val1); + int plid2 = NCDVal_PlaceholderId(val2); + + return (plid1 > plid2) - (plid1 < plid2); + } break; + + default: + ASSERT(0); + return 0; + } +} + +NCDValSafeRef NCDVal_ToSafe (NCDValRef val) +{ + NCDVal_Assert(val); + + NCDValSafeRef sval = {val.idx}; + return sval; +} + +NCDValRef NCDVal_FromSafe (NCDValMem *mem, NCDValSafeRef sval) +{ + NCDVal__AssertMem(mem); + ASSERT(sval.idx == -1 || (NCDVal__AssertValOnly(mem, sval.idx), 1)) + + NCDValRef val = {mem, sval.idx}; + return val; +} + +NCDValRef NCDVal_Moved (NCDValMem *mem, NCDValRef val) +{ + NCDVal__AssertMem(mem); + ASSERT(val.idx == -1 || (NCDVal__AssertValOnly(mem, val.idx), 1)) + + NCDValRef val2 = {mem, val.idx}; + return val2; +} + +int NCDVal_HasOnlyContinuousStrings (NCDValRef val) +{ + NCDVal__AssertVal(val); + + switch (NCDVal_Type(val)) { + case NCDVAL_STRING: { + if (!NCDVal_IsContinuousString(val)) { + return 0; + } + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(val); + for (size_t i = 0; i < count; i++) { + NCDValRef elem = NCDVal_ListGet(val, i); + if (!NCDVal_HasOnlyContinuousStrings(elem)) { + return 0; + } + } + } break; + + case NCDVAL_MAP: { + for (NCDValMapElem me = NCDVal_MapFirst(val); !NCDVal_MapElemInvalid(me); me = NCDVal_MapNext(val, me)) { + NCDValRef e_key = NCDVal_MapElemKey(val, me); + NCDValRef e_val = NCDVal_MapElemVal(val, me); + if (!NCDVal_HasOnlyContinuousStrings(e_key) || !NCDVal_HasOnlyContinuousStrings(e_val)) { + return 0; + } + } + } break; + + case NCDVAL_PLACEHOLDER: { + } break; + + default: + ASSERT(0); + } + + return 1; +} + +int NCDVal_IsString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_STRING; +} + +int NCDVal_IsContinuousString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return 0; + } + + switch (get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx))) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + return 1; + default: + return 0; + } +} + +int NCDVal_IsStoredString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == STOREDSTRING_TYPE; +} + +int NCDVal_IsIdString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == IDSTRING_TYPE; +} + +int NCDVal_IsExternalString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == EXTERNALSTRING_TYPE; +} + +int NCDVal_IsComposedString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == COMPOSEDSTRING_TYPE; +} + +int NCDVal_IsStringNoNulls (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_STRING && !NCDVal_StringHasNulls(val); +} + +NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data) +{ + NCDVal__AssertMem(mem); + ASSERT(data) + NCDVal_AssertExternal(mem, data, strlen(data)); + + return NCDVal_NewStringBin(mem, (const uint8_t *)data, strlen(data)); +} + +NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len) +{ + NCDVal__AssertMem(mem); + ASSERT(len == 0 || data) + NCDVal_AssertExternal(mem, data, len); + + if (len > NCDVAL_MAXIDX - sizeof(struct NCDVal__string) - 1) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__string) + len + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + str_e->type = make_type(STOREDSTRING_TYPE, 0); + str_e->length = len; + if (len > 0) { + memcpy(str_e->data, data, len); + } + str_e->data[len] = '\0'; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len) +{ + NCDVal__AssertMem(mem); + + if (len > NCDVAL_MAXIDX - sizeof(struct NCDVal__string) - 1) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__string) + len + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + str_e->type = make_type(STOREDSTRING_TYPE, 0); + str_e->length = len; + str_e->data[len] = '\0'; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewIdString (NCDValMem *mem, NCD_string_id_t string_id, NCDStringIndex *string_index) +{ + NCDVal__AssertMem(mem); + ASSERT(string_id >= 0) + ASSERT(string_index) + + NCDVal__idx size = sizeof(struct NCDVal__idstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__idstring)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(mem, idx); + ids_e->type = make_type(IDSTRING_TYPE, 0); + ids_e->string_id = string_id; + ids_e->string_index = string_index; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len, + BRefTarget *ref_target) +{ + NCDVal__AssertMem(mem); + ASSERT(data) + NCDVal_AssertExternal(mem, data, len); + + NCDVal__idx size = sizeof(struct NCDVal__externalstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__externalstring)); + if (idx < 0) { + goto fail; + } + + if (ref_target) { + if (!BRefTarget_Ref(ref_target)) { + goto fail; + } + } + + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx); + exs_e->type = make_type(EXTERNALSTRING_TYPE, 0); + exs_e->data = data; + exs_e->length = len; + exs_e->ref.target = ref_target; + + if (ref_target) { + exs_e->ref.next = mem->first_ref; + mem->first_ref = idx + offsetof(struct NCDVal__externalstring, ref); + } + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewComposedString (NCDValMem *mem, NCDValComposedStringResource resource, size_t offset, size_t length) +{ + NCDVal__AssertMem(mem); + ASSERT(resource.func_getptr) + + NCDVal__idx size = sizeof(struct NCDVal__composedstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__composedstring)); + if (idx < 0) { + goto fail; + } + + if (resource.ref_target) { + if (!BRefTarget_Ref(resource.ref_target)) { + goto fail; + } + } + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(mem, idx); + cms_e->type = make_type(COMPOSEDSTRING_TYPE, 0); + cms_e->offset = offset; + cms_e->length = length; + cms_e->func_getptr = resource.func_getptr; + cms_e->user = resource.user; + cms_e->ref.target = resource.ref_target; + + if (resource.ref_target) { + cms_e->ref.next = mem->first_ref; + mem->first_ref = idx + offsetof(struct NCDVal__composedstring, ref); + } + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +const char * NCDVal_StringData (NCDValRef contstring) +{ + ASSERT(NCDVal_IsContinuousString(contstring)) + + void *ptr = NCDValMem__BufAt(contstring.mem, contstring.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return str_e->data; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id); + return value; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return exs_e->data; + } break; + + default: + ASSERT(0); + return NULL; + } +} + +size_t NCDVal_StringLength (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return str_e->length; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return NCDStringIndex_Length(ids_e->string_index, ids_e->string_id); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return exs_e->length; + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + return cms_e->length; + } break; + + default: + ASSERT(0); + return 0; + } +} + +b_cstring NCDValComposedStringResource_Cstring (NCDValComposedStringResource resource, size_t offset, size_t length) +{ + b_cstring cstr; + cstr.length = length; + cstr.func = NCDVal__composedstring_cstring_func; + cstr.user1.size = offset; + cstr.user2.fptr = (void (*) (void))resource.func_getptr; + cstr.user3.ptr = resource.user; + return cstr; +} + +b_cstring NCDVal_StringCstring (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return b_cstring_make_buf(str_e->data, str_e->length); + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return b_cstring_make_buf(NCDStringIndex_Value(ids_e->string_index, ids_e->string_id), NCDStringIndex_Length(ids_e->string_index, ids_e->string_id)); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return b_cstring_make_buf(exs_e->data, exs_e->length); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + b_cstring cstr; + cstr.length = cms_e->length; + cstr.func = NCDVal__composedstring_cstring_func; + cstr.user1.size = cms_e->offset; + cstr.user2.fptr = (void (*) (void))cms_e->func_getptr; + cstr.user3.ptr = cms_e->user; + return cstr; + } break; + + default: { + ASSERT(0); + return b_cstring_make_empty(); + } break; + } +} + +int NCDVal_StringNullTerminate (NCDValRef string, NCDValNullTermString *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + out->data = str_e->data; + out->is_allocated = 0; + return 1; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + out->data = (char *)NCDStringIndex_Value(ids_e->string_index, ids_e->string_id); + out->is_allocated = 0; + return 1; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + + char *copy = b_strdup_bin(exs_e->data, exs_e->length); + if (!copy) { + return 0; + } + + out->data = copy; + out->is_allocated = 1; + return 1; + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + size_t length = cms_e->length; + + if (length == SIZE_MAX) { + return 0; + } + + char *copy = BAlloc(length + 1); + if (!copy) { + return 0; + } + + NCDVal_StringCopyOut(string, 0, length, copy); + copy[length] = '\0'; + + out->data = copy; + out->is_allocated = 1; + return 1; + } break; + + default: + ASSERT(0); + return 0; + } +} + +NCDValNullTermString NCDValNullTermString_NewDummy (void) +{ + NCDValNullTermString nts; + nts.data = NULL; + nts.is_allocated = 0; + return nts; +} + +void NCDValNullTermString_Free (NCDValNullTermString *o) +{ + if (o->is_allocated) { + BFree(o->data); + } +} + +int NCDVal_StringContinuize (NCDValRef string, NCDValContString *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + if (NCDVal_IsContinuousString(string)) { + out->data = (char *)NCDVal_StringData(string); + out->is_allocated = 0; + return 1; + } + + size_t length = NCDVal_StringLength(string); + + char *data = BAlloc(length); + if (!data) { + return 0; + } + + NCDVal_StringCopyOut(string, 0, length, data); + + out->data = data; + out->is_allocated = 1; + return 1; +} + +NCDValContString NCDValContString_NewDummy (void) +{ + NCDValContString cts; + cts.data = NULL; + cts.is_allocated = 0; + return cts; +} + +void NCDValContString_Free (NCDValContString *o) +{ + if (o->is_allocated) { + BFree(o->data); + } +} + +void NCDVal_IdStringGet (NCDValRef idstring, NCD_string_id_t *out_string_id, + NCDStringIndex **out_string_index) +{ + ASSERT(NCDVal_IsIdString(idstring)) + ASSERT(out_string_id) + ASSERT(out_string_index) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + *out_string_id = ids_e->string_id; + *out_string_index = ids_e->string_index; +} + +NCD_string_id_t NCDVal_IdStringId (NCDValRef idstring) +{ + ASSERT(NCDVal_IsIdString(idstring)) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + return ids_e->string_id; +} + +NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring) +{ + ASSERT(NCDVal_IsIdString(idstring)) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + return ids_e->string_index; +} + +BRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring) +{ + ASSERT(NCDVal_IsExternalString(externalstring)) + + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(externalstring.mem, externalstring.idx); + return exs_e->ref.target; +} + +NCDValComposedStringResource NCDVal_ComposedStringResource (NCDValRef composedstring) +{ + ASSERT(NCDVal_IsComposedString(composedstring)) + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(composedstring.mem, composedstring.idx); + + NCDValComposedStringResource res; + res.func_getptr = cms_e->func_getptr; + res.user = cms_e->user; + res.ref_target = cms_e->ref.target; + + return res; +} + +size_t NCDVal_ComposedStringOffset (NCDValRef composedstring) +{ + ASSERT(NCDVal_IsComposedString(composedstring)) + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(composedstring.mem, composedstring.idx); + + return cms_e->offset; +} + +int NCDVal_StringHasNulls (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return NCDStringIndex_HasNulls(ids_e->string_index, ids_e->string_id); + } break; + + case STOREDSTRING_TYPE: + case EXTERNALSTRING_TYPE: { + const char *data = NCDVal_StringData(string); + size_t length = NCDVal_StringLength(string); + return !!memchr(data, '\0', length); + } break; + + case COMPOSEDSTRING_TYPE: { + b_cstring cstr = NCDVal_StringCstring(string); + return b_cstring_memchr(cstr, 0, cstr.length, '\0', NULL); + } break; + + default: + ASSERT(0); + return 0; + } +} + +int NCDVal_StringEquals (NCDValRef string, const char *data) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(data) + + size_t data_len = strlen(data); + + return NCDVal_StringLength(string) == data_len && NCDVal_StringRegionEquals(string, 0, data_len, data); +} + +int NCDVal_StringEqualsId (NCDValRef string, NCD_string_id_t string_id, + NCDStringIndex *string_index) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(string_id >= 0) + ASSERT(string_index) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == str_e->length) && !memcmp(string_data, str_e->data, string_length); + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + ASSERT(ids_e->string_index == string_index) + return ids_e->string_id == string_id; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == exs_e->length) && !memcmp(string_data, exs_e->data, string_length); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == cms_e->length) && NCDVal_StringRegionEquals(string, 0, string_length, string_data); + } break; + + default: + ASSERT(0); + return 0; + } +} + +int NCDVal_StringMemCmp (NCDValRef string1, NCDValRef string2, size_t start1, size_t start2, size_t length) +{ + ASSERT(NCDVal_IsString(string1)) + ASSERT(NCDVal_IsString(string2)) + ASSERT(start1 <= NCDVal_StringLength(string1)) + ASSERT(start2 <= NCDVal_StringLength(string2)) + ASSERT(length <= NCDVal_StringLength(string1) - start1) + ASSERT(length <= NCDVal_StringLength(string2) - start2) + + if (NCDVal_IsContinuousString(string1) && NCDVal_IsContinuousString(string2)) { + return memcmp(NCDVal_StringData(string1) + start1, NCDVal_StringData(string2) + start2, length); + } + + b_cstring cstr1 = NCDVal_StringCstring(string1); + b_cstring cstr2 = NCDVal_StringCstring(string2); + return b_cstring_memcmp(cstr1, cstr2, start1, start2, length); +} + +void NCDVal_StringCopyOut (NCDValRef string, size_t start, size_t length, char *dst) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(start <= NCDVal_StringLength(string)) + ASSERT(length <= NCDVal_StringLength(string) - start) + + if (NCDVal_IsContinuousString(string)) { + memcpy(dst, NCDVal_StringData(string) + start, length); + return; + } + + b_cstring cstr = NCDVal_StringCstring(string); + b_cstring_copy_to_buf(cstr, start, length, dst); +} + +int NCDVal_StringRegionEquals (NCDValRef string, size_t start, size_t length, const char *data) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(start <= NCDVal_StringLength(string)) + ASSERT(length <= NCDVal_StringLength(string) - start) + + if (NCDVal_IsContinuousString(string)) { + return !memcmp(NCDVal_StringData(string) + start, data, length); + } + + b_cstring cstr = NCDVal_StringCstring(string); + return b_cstring_equals_buffer(cstr, start, length, data); +} + +int NCDVal_IsList (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_LIST; +} + +NCDValRef NCDVal_NewList (NCDValMem *mem, size_t maxcount) +{ + NCDVal__AssertMem(mem); + + if (maxcount > (NCDVAL_MAXIDX - sizeof(struct NCDVal__list)) / sizeof(NCDVal__idx)) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__list) + maxcount * sizeof(NCDVal__idx); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__list)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__list *list_e = NCDValMem__BufAt(mem, idx); + list_e->type = make_type(NCDVAL_LIST, 0); + list_e->maxcount = maxcount; + list_e->count = 0; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_ListAppend (NCDValRef list, NCDValRef elem) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(NCDVal_ListCount(list) < NCDVal_ListMaxCount(list)) + ASSERT(elem.mem == list.mem) + NCDVal__AssertValOnly(list.mem, elem.idx); + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + int new_type = list_e->type; + if (!bump_depth(&new_type, NCDVal__Depth(elem))) { + return 0; + } + + if (NCDValMem__NeedRegisterLink(list.mem, elem.idx)) { + if (!NCDValMem__RegisterLink(list.mem, elem.idx, list.idx + offsetof(struct NCDVal__list, elem_indices) + list_e->count * sizeof(NCDVal__idx))) { + return 0; + } + list_e = NCDValMem__BufAt(list.mem, list.idx); + } + + list_e->type = new_type; + list_e->elem_indices[list_e->count++] = elem.idx; + + return 1; +} + +size_t NCDVal_ListCount (NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + return list_e->count; +} + +size_t NCDVal_ListMaxCount (NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + return list_e->maxcount; +} + +NCDValRef NCDVal_ListGet (NCDValRef list, size_t pos) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(pos < NCDVal_ListCount(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + ASSERT(pos < list_e->count) + NCDVal__AssertValOnly(list.mem, list_e->elem_indices[pos]); + + return NCDVal__Ref(list.mem, list_e->elem_indices[pos]); +} + +int NCDVal_ListRead (NCDValRef list, int num, ...) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(num >= 0) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + if (num != list_e->count) { + return 0; + } + + va_list ap; + va_start(ap, num); + + for (int i = 0; i < num; i++) { + NCDValRef *dest = va_arg(ap, NCDValRef *); + *dest = NCDVal__Ref(list.mem, list_e->elem_indices[i]); + } + + va_end(ap); + + return 1; +} + +int NCDVal_ListReadHead (NCDValRef list, int num, ...) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(num >= 0) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + if (num > list_e->count) { + return 0; + } + + va_list ap; + va_start(ap, num); + + for (int i = 0; i < num; i++) { + NCDValRef *dest = va_arg(ap, NCDValRef *); + *dest = NCDVal__Ref(list.mem, list_e->elem_indices[i]); + } + + va_end(ap); + + return 1; +} + +int NCDVal_IsMap (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_MAP; +} + +NCDValRef NCDVal_NewMap (NCDValMem *mem, size_t maxcount) +{ + NCDVal__AssertMem(mem); + + if (maxcount > (NCDVAL_MAXIDX - sizeof(struct NCDVal__map)) / sizeof(struct NCDVal__mapelem)) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__map) + maxcount * sizeof(struct NCDVal__mapelem); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__map)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, idx); + map_e->type = make_type(NCDVAL_MAP, 0); + map_e->maxcount = maxcount; + map_e->count = 0; + NCDVal__MapTree_Init(&map_e->tree); + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_MapInsert (NCDValRef map, NCDValRef key, NCDValRef val, int *out_inserted) +{ + ASSERT(NCDVal_IsMap(map)) + ASSERT(NCDVal_MapCount(map) < NCDVal_MapMaxCount(map)) + ASSERT(key.mem == map.mem) + ASSERT(val.mem == map.mem) + NCDVal__AssertValOnly(map.mem, key.idx); + NCDVal__AssertValOnly(map.mem, val.idx); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + int new_type = map_e->type; + if (!bump_depth(&new_type, NCDVal__Depth(key)) || !bump_depth(&new_type, NCDVal__Depth(val))) { + goto fail0; + } + + NCDVal__idx elemidx = NCDVal__MapElemIdx(map.idx, map_e->count); + + if (NCDValMem__NeedRegisterLink(map.mem, key.idx)) { + if (!NCDValMem__RegisterLink(map.mem, key.idx, elemidx + offsetof(struct NCDVal__mapelem, key_idx))) { + goto fail0; + } + map_e = NCDValMem__BufAt(map.mem, map.idx); + } + + if (NCDValMem__NeedRegisterLink(map.mem, val.idx)) { + if (!NCDValMem__RegisterLink(map.mem, val.idx, elemidx + offsetof(struct NCDVal__mapelem, val_idx))) { + goto fail1; + } + map_e = NCDValMem__BufAt(map.mem, map.idx); + } + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, elemidx); + ASSERT(me_e == &map_e->elems[map_e->count]) + me_e->key_idx = key.idx; + me_e->val_idx = val.idx; + + int res = NCDVal__MapTree_Insert(&map_e->tree, map.mem, NCDVal__MapTreeDeref(map.mem, elemidx), NULL); + if (!res) { + if (out_inserted) { + *out_inserted = 0; + } + return 1; + } + + map_e->type = new_type; + map_e->count++; + + if (out_inserted) { + *out_inserted = 1; + } + return 1; + +fail1: + if (NCDValMem__NeedRegisterLink(map.mem, key.idx)) { + NCDValMem__PopLastRegisteredLink(map.mem); + } +fail0: + return 0; +} + +size_t NCDVal_MapCount (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + return map_e->count; +} + +size_t NCDVal_MapMaxCount (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + return map_e->maxcount; +} + +int NCDVal_MapElemInvalid (NCDValMapElem me) +{ + ASSERT(me.elemidx >= 0 || me.elemidx == -1) + + return me.elemidx < 0; +} + +NCDValMapElem NCDVal_MapFirst (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + if (map_e->count == 0) { + return NCDVal__MapElem(-1); + } + + NCDVal__idx elemidx = NCDVal__MapElemIdx(map.idx, 0); + NCDVal__MapAssertElemOnly(map, elemidx); + + return NCDVal__MapElem(elemidx); +} + +NCDValMapElem NCDVal_MapNext (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + ASSERT(map_e->count > 0) + + NCDVal__idx last_elemidx = NCDVal__MapElemIdx(map.idx, map_e->count - 1); + ASSERT(me.elemidx <= last_elemidx) + + if (me.elemidx == last_elemidx) { + return NCDVal__MapElem(-1); + } + + NCDVal__idx elemidx = me.elemidx + sizeof(struct NCDVal__mapelem); + NCDVal__MapAssertElemOnly(map, elemidx); + + return NCDVal__MapElem(elemidx); +} + +NCDValMapElem NCDVal_MapOrderedFirst (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_GetFirst(&map_e->tree, map.mem); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValMapElem NCDVal_MapOrderedNext (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_GetNext(&map_e->tree, map.mem, NCDVal__MapTreeDeref(map.mem, me.elemidx)); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValRef NCDVal_MapElemKey (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, me.elemidx); + + return NCDVal__Ref(map.mem, me_e->key_idx); +} + +NCDValRef NCDVal_MapElemVal (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, me.elemidx); + + return NCDVal__Ref(map.mem, me_e->val_idx); +} + +NCDValMapElem NCDVal_MapFindKey (NCDValRef map, NCDValRef key) +{ + ASSERT(NCDVal_IsMap(map)) + NCDVal__AssertVal(key); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_LookupExact(&map_e->tree, map.mem, key); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValRef NCDVal_MapGetValue (NCDValRef map, const char *key_str) +{ + ASSERT(NCDVal_IsMap(map)) + ASSERT(key_str) + + NCDValMem mem; + mem.buf = NULL; + mem.size = NCDVAL_FASTBUF_SIZE; + mem.used = sizeof(struct NCDVal__externalstring); + mem.first_ref = -1; + + struct NCDVal__externalstring *exs_e = (void *)mem.fastbuf; + exs_e->type = make_type(EXTERNALSTRING_TYPE, 0); + exs_e->data = key_str; + exs_e->length = strlen(key_str); + exs_e->ref.target = NULL; + + NCDValRef key = NCDVal__Ref(&mem, 0); + + NCDValMapElem elem = NCDVal_MapFindKey(map, key); + if (NCDVal_MapElemInvalid(elem)) { + return NCDVal_NewInvalid(); + } + + return NCDVal_MapElemVal(map, elem); +} + +static void replaceprog_build_recurser (NCDValMem *mem, NCDVal__idx idx, size_t *out_num_instr, NCDValReplaceProg *prog) +{ + ASSERT(idx >= 0) + NCDVal__AssertValOnly(mem, idx); + ASSERT(out_num_instr) + + *out_num_instr = 0; + + void *ptr = NCDValMem__BufAt(mem, idx); + + struct NCDVal__instr instr; + + switch (get_internal_type(*((int *)(ptr)))) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + } break; + + case NCDVAL_LIST: { + struct NCDVal__list *list_e = ptr; + + for (NCDVal__idx i = 0; i < list_e->count; i++) { + int elem_changed = 0; + + if (list_e->elem_indices[i] < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = list_e->elem_indices[i] - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + elem_changed = 1; + } else { + size_t elem_num_instr; + replaceprog_build_recurser(mem, list_e->elem_indices[i], &elem_num_instr, prog); + (*out_num_instr) += elem_num_instr; + if (elem_num_instr > 0) { + elem_changed = 1; + } + } + + if (elem_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + } + } break; + + case NCDVAL_MAP: { + struct NCDVal__map *map_e = ptr; + + for (NCDVal__idx i = 0; i < map_e->count; i++) { + int key_changed = 0; + int val_changed = 0; + + if (map_e->elems[i].key_idx < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = map_e->elems[i].key_idx - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, key_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + key_changed = 1; + } else { + size_t key_num_instr; + replaceprog_build_recurser(mem, map_e->elems[i].key_idx, &key_num_instr, prog); + (*out_num_instr) += key_num_instr; + if (key_num_instr > 0) { + key_changed = 1; + } + } + + if (map_e->elems[i].val_idx < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = map_e->elems[i].val_idx - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, val_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + val_changed = 1; + } else { + size_t val_num_instr; + replaceprog_build_recurser(mem, map_e->elems[i].val_idx, &val_num_instr, prog); + (*out_num_instr) += val_num_instr; + if (val_num_instr > 0) { + val_changed = 1; + } + } + + if (key_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_REINSERT; + instr.reinsert.mapidx = idx; + instr.reinsert.elempos = i; + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, key_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + + if (val_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, val_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + } + } break; + + default: ASSERT(0); + } +} + +int NCDValReplaceProg_Init (NCDValReplaceProg *o, NCDValRef val) +{ + NCDVal__AssertVal(val); + ASSERT(!NCDVal_IsPlaceholder(val)) + + size_t num_instrs; + replaceprog_build_recurser(val.mem, val.idx, &num_instrs, NULL); + + if (!(o->instrs = BAllocArray(num_instrs, sizeof(o->instrs[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + return 0; + } + + o->num_instrs = 0; + + size_t num_instrs2; + replaceprog_build_recurser(val.mem, val.idx, &num_instrs2, o); + + ASSERT(num_instrs2 == num_instrs) + ASSERT(o->num_instrs == num_instrs) + + return 1; +} + +void NCDValReplaceProg_Free (NCDValReplaceProg *o) +{ + BFree(o->instrs); +} + +int NCDValReplaceProg_Execute (NCDValReplaceProg prog, NCDValMem *mem, NCDVal_replace_func replace, void *arg) +{ + NCDVal__AssertMem(mem); + ASSERT(replace) + + for (size_t i = 0; i < prog.num_instrs; i++) { + struct NCDVal__instr instr = prog.instrs[i]; + + switch (instr.type) { + case NCDVAL_INSTR_PLACEHOLDER: { +#ifndef NDEBUG + NCDVal__idx *check_plptr = NCDValMem__BufAt(mem, instr.placeholder.plidx); + ASSERT(*check_plptr < -1) + ASSERT(*check_plptr - NCDVAL_MINIDX == instr.placeholder.plid) +#endif + NCDValRef repval; + if (!replace(arg, instr.placeholder.plid, mem, &repval) || NCDVal_IsInvalid(repval)) { + return 0; + } + ASSERT(repval.mem == mem) + + if (NCDValMem__NeedRegisterLink(mem, repval.idx)) { + NCDValMem__RegisterLink(mem, repval.idx, instr.placeholder.plidx); + } + + NCDVal__idx *plptr = NCDValMem__BufAt(mem, instr.placeholder.plidx); + *plptr = repval.idx; + } break; + + case NCDVAL_INSTR_REINSERT: { + NCDVal__AssertValOnly(mem, instr.reinsert.mapidx); + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, instr.reinsert.mapidx); + ASSERT(map_e->type == NCDVAL_MAP) + ASSERT(instr.reinsert.elempos >= 0) + ASSERT(instr.reinsert.elempos < map_e->count) + + NCDVal__MapTreeRef ref = {&map_e->elems[instr.reinsert.elempos], NCDVal__MapElemIdx(instr.reinsert.mapidx, instr.reinsert.elempos)}; + NCDVal__MapTree_Remove(&map_e->tree, mem, ref); + if (!NCDVal__MapTree_Insert(&map_e->tree, mem, ref, NULL)) { + BLog(BLOG_ERROR, "duplicate key in map"); + return 0; + } + } break; + + case NCDVAL_INSTR_BUMPDEPTH: { + NCDVal__AssertValOnly(mem, instr.bumpdepth.parent_idx); + int *parent_type_ptr = NCDValMem__BufAt(mem, instr.bumpdepth.parent_idx); + + NCDVal__idx *child_type_idx_ptr = NCDValMem__BufAt(mem, instr.bumpdepth.child_idx_idx); + NCDVal__AssertValOnly(mem, *child_type_idx_ptr); + int *child_type_ptr = NCDValMem__BufAt(mem, *child_type_idx_ptr); + + if (!bump_depth(parent_type_ptr, get_depth(*child_type_ptr))) { + BLog(BLOG_ERROR, "depth limit exceeded"); + return 0; + } + } break; + + default: ASSERT(0); + } + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDVal.h b/app/src/main/jni/badvpn/ncd/NCDVal.h new file mode 100644 index 0000000..26154da --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDVal.h @@ -0,0 +1,857 @@ +/** + * @file NCDVal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVAL_H +#define BADVPN_NCDVAL_H + +#include +#include + +#include +#include +#include +#include +#include + +// these are implementation details. The interface is defined below. + +#define NCDVAL_FASTBUF_SIZE 64 +#define NCDVAL_FIRST_SIZE 256 +#define NCDVAL_MAX_DEPTH 32 + +#define NCDVAL_MAXIDX INT_MAX +#define NCDVAL_MINIDX INT_MIN + +typedef int NCDVal__idx; + +struct NCDVal__ref { + NCDVal__idx next; + BRefTarget *target; +}; + +struct NCDVal__string { + int type; + NCDVal__idx length; + char data[]; +}; + +struct NCDVal__list { + int type; + NCDVal__idx maxcount; + NCDVal__idx count; + NCDVal__idx elem_indices[]; +}; + +struct NCDVal__mapelem { + NCDVal__idx key_idx; + NCDVal__idx val_idx; + NCDVal__idx tree_child[2]; + NCDVal__idx tree_parent; + int8_t tree_balance; +}; + +struct NCDVal__idstring { + int type; + NCD_string_id_t string_id; + NCDStringIndex *string_index; +}; + +struct NCDVal__externalstring { + int type; + const char *data; + size_t length; + struct NCDVal__ref ref; +}; + +struct NCDVal__composedstring { + int type; + size_t offset; + size_t length; + void (*func_getptr) (void *, size_t, const char **, size_t *); + void *user; + struct NCDVal__ref ref; +}; + +struct NCDVal__cms_link { + NCDVal__idx link_idx; + NCDVal__idx next_cms_link; +}; + +typedef struct { + char *buf; + NCDVal__idx size; + NCDVal__idx used; + NCDVal__idx first_ref; + NCDVal__idx first_cms_link; + union { + char fastbuf[NCDVAL_FASTBUF_SIZE]; + struct NCDVal__ref align_ref; + struct NCDVal__string align_string; + struct NCDVal__list align_list; + struct NCDVal__mapelem align_mapelem; + struct NCDVal__idstring align_idstring; + struct NCDVal__externalstring align_externalstring; + struct NCDVal__composedstring align_composedstring; + struct NCDVal__cms_link align_cms_link; + }; +} NCDValMem; + +typedef struct { + NCDValMem *mem; + NCDVal__idx idx; +} NCDValRef; + +typedef struct { + NCDVal__idx idx; +} NCDValSafeRef; + +typedef struct NCDVal__mapelem NCDVal__maptree_entry; +typedef NCDValMem *NCDVal__maptree_arg; + +#include "NCDVal_maptree.h" +#include + +struct NCDVal__map { + int type; + NCDVal__idx maxcount; + NCDVal__idx count; + NCDVal__MapTree tree; + struct NCDVal__mapelem elems[]; +}; + +typedef struct { + NCDVal__idx elemidx; +} NCDValMapElem; + +#define NCDVAL_INSTR_PLACEHOLDER 0 +#define NCDVAL_INSTR_REINSERT 1 +#define NCDVAL_INSTR_BUMPDEPTH 2 + +struct NCDVal__instr { + int type; + union { + struct { + NCDVal__idx plid; + NCDVal__idx plidx; + } placeholder; + struct { + NCDVal__idx mapidx; + NCDVal__idx elempos; + } reinsert; + struct { + NCDVal__idx parent_idx; + NCDVal__idx child_idx_idx; + } bumpdepth; + }; +}; + +typedef struct { + struct NCDVal__instr *instrs; + size_t num_instrs; +} NCDValReplaceProg; + +typedef struct { + char *data; + int is_allocated; +} NCDValNullTermString; + +typedef struct { + char *data; + int is_allocated; +} NCDValContString; + +// + +#define NCDVAL_STRING 1 +#define NCDVAL_LIST 2 +#define NCDVAL_MAP 3 +#define NCDVAL_PLACEHOLDER 4 + +/** + * Initializes a value memory object. + * A value memory object holds memory for value structures. Values within + * the memory are referenced using {@link NCDValRef} objects, which point + * to values within memory objects. + * + * Values may be added to a memory object using functions such as + * {@link NCDVal_NewString}, {@link NCDVal_NewList} and {@link NCDVal_NewMap}, + * and {@link NCDVal_NewCopy}, which return references to the new values within + * the memory object. + * + * It is not possible to remove values from the memory object, or modify existing + * values other than adding elements to pre-allocated slots in lists and maps. + * Once a value is added, it will consume memory as long as its memory object + * exists. This is by design - this code is intended and optimized for constructing + * and passing around values, not for operating on them in place. In fact, al + * values within a memory object are stored in a single memory buffer, as an + * embedded data structure with relativepointers. For example, map values use an + * embedded AVL tree. + */ +void NCDValMem_Init (NCDValMem *o); + +/** + * Frees a value memory object. + * All values within the memory object cease to exist, and any {@link NCDValRef} + * object pointing to them must no longer be used. + */ +void NCDValMem_Free (NCDValMem *o); + +/** + * Initializes the memory object to be a copy of an existing memory object. + * Value references from the original may be used if they are first turned + * to {@link NCDValSafeRef} using {@link NCDVal_ToSafe} and back to + * {@link NCDValRef} using {@link NCDVal_FromSafe} with the new memory object + * specified. Alternatively, {@link NCDVal_Moved} can be used. + * Returns 1 on success and 0 on failure. + */ +int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other) WARN_UNUSED; + +/** + * For each internal link (e.g. list element) to a ComposedString in the memory + * object, copies the ComposedString to some kind ContinuousString, and updates + * the link to point to the new ContinuousString. + * Additionally, if *\a root_val points to a ComposedString, copies it to a new + * ContinuousString and updates *\a root_val to point to it. + * \a root_val must be non-NULL and *\a root_val must not be an invalid value + * reference. + * Returns 1 on success and 0 on failure. On failure, some strings may have + * been converted, but the memory object is left in a consistent state. + */ +int NCDValMem_ConvertNonContinuousStrings (NCDValMem *o, NCDValRef *root_val) WARN_UNUSED; + +/** + * Does nothing. + * The value reference object must either point to a valid value within a valid + * memory object, or must be an invalid reference (most functions operating on + * {@link NCDValRef} implicitly require that). + */ +void NCDVal_Assert (NCDValRef val); + +/** + * Determines if a value reference is invalid. + */ +int NCDVal_IsInvalid (NCDValRef val); + +/** + * Determines if a value is a placeholder value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsPlaceholder (NCDValRef val); + +/** + * Returns the type of the value reference, which must not be an invalid reference. + * Possible values are NCDVAL_STRING, NCDVAL_LIST, NCDVAL_MAP and NCDVAL_PLACEHOLDER. + * The placeholder type is only used internally in the interpreter for argument + * resolution, and is never seen by modules; see {@link NCDVal_NewPlaceholder}. + */ +int NCDVal_Type (NCDValRef val); + +/** + * Returns an invalid reference. + * An invalid reference must not be passed to any function here, except: + * {@link NCDVal_Assert}, {@link NCDVal_IsInvalid}, {@link NCDVal_ToSafe}, + * {@link NCDVal_FromSafe}, {@link NCDVal_Moved}. + */ +NCDValRef NCDVal_NewInvalid (void); + +/** + * Returns a new placeholder value reference. A placeholder value is a valid value + * containing an integer placeholder identifier. + * This always succeeds; however, the caller must ensure the identifier is + * non-negative and satisfies (NCDVAL_MINIDX + plid < -1). + * + * The placeholder type is only used internally in the interpreter for argument + * resolution, and is never seen by modules. Also see {@link NCDPlaceholderDb}. + */ +NCDValRef NCDVal_NewPlaceholder (NCDValMem *mem, int plid); + +/** + * Returns the indentifier of a placeholder value. + * The value reference must point to a placeholder value. + */ +int NCDVal_PlaceholderId (NCDValRef val); + +/** + * Copies a value into the specified memory object. The source + * must not be an invalid reference, however it may reside in any memory + * object (including 'mem'). + * Returns a reference to the copied value. On out of memory, returns + * an invalid reference. + */ +NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val); + +/** + * Compares two values, both of which must not be invalid references. + * Returns -1, 0 or 1. + */ +int NCDVal_Compare (NCDValRef val1, NCDValRef val2); + +/** + * Converts a value reference to a safe referece format, which remains valid + * if the memory object is moved (safe references do not contain a pointer + * to the memory object, unlike {@link NCDValRef} references). + */ +NCDValSafeRef NCDVal_ToSafe (NCDValRef val); + +/** + * Converts a safe value reference to a normal value reference. + * This should be used to recover references from safe references + * after the memory object is moved. + */ +NCDValRef NCDVal_FromSafe (NCDValMem *mem, NCDValSafeRef sval); + +/** + * Fixes a value reference after its memory object was moved. + */ +NCDValRef NCDVal_Moved (NCDValMem *mem, NCDValRef val); + +/** + * Determines if all strings within this value are ContinuousString's, + * by recusively walking the entire value. + * If all strings are ContinuousString's, returns 1; if there is at least + * one string which is not a ContinuousString, returns 0. + * The value reference must not be an invalid reference. + */ +int NCDVal_HasOnlyContinuousStrings (NCDValRef val); + +/** + * Determines if the value implements the String interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsString (NCDValRef val); + +/** + * Determines if the value implements the ContinuousString interface. + * A ContinuousString also implements the String interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsContinuousString (NCDValRef val); + +/** + * Determines if the value is a StoredString. + * A StoredString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsStoredString (NCDValRef val); + +/** + * Determines if the value is an IdString. See {@link NCDVal_NewIdString} + * for details. + * An IdString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsIdString (NCDValRef val); + +/** + * Determines if a value is an ExternalString. + * See {@link NCDVal_NewExternalString} for details. + * An ExternalString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsExternalString (NCDValRef val); + +/** + * Determines if a value is a ComposedString. + * A ComposedString implements the String interface. + */ +int NCDVal_IsComposedString (NCDValRef val); + +/** + * Determines if a value is a String which contains no null bytes. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsStringNoNulls (NCDValRef val); + +/** + * Equivalent to NCDVal_NewStringBin(mem, data, strlen(data)). + */ +NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data); + +/** + * Builds a new StoredString. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * WARNING: The buffer passed must NOT be part of any value in the + * memory object specified. In particular, you may NOT use this + * function to copy a string that resides in the same memory object. + * + * A StoredString is a kind of ContinuousString which is represented directly in the + * value memory object. + */ +NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len); + +/** + * Builds a new StoredString of the given length with undefined contents. + * You can define the contents of the string later by copying to the address + * returned by {@link NCDVal_StringData}. + */ +NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len); + +/** + * Builds a new IdString. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * + * An IdString is a kind of ContinuousString which is represented efficiently as a string + * identifier via {@link NCDStringIndex}. + */ +NCDValRef NCDVal_NewIdString (NCDValMem *mem, NCD_string_id_t string_id, + NCDStringIndex *string_index); + +/** + * Builds a new ExternalString, pointing to the given external data. A reference to + * the external data is taken using {@link BRefTarget}, unless 'ref_target' is + * NULL. The data must not change while this value exists. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * + * An ExternalString is a kind of ContinuousString where the actual string contents are + * stored outside of the value memory object. + */ +NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len, + BRefTarget *ref_target); + +/** + * Callback function which is called for ComposedString's to access the underlying string resource. + * \a user is whatever was passed to 'resource.user' in {@link NCDVal_NewComposedString}. + * \a offset is the offset from the beginning of the string exposed by the resource; it will be + * >= 'offset' and < 'offset' + 'length' as given to NCDVal_NewComposedString. + * This callback must set *\a out_data and *\a out_length to represent a continuous (sub-)region + * of the string that starts at the byte at index \a offset. The pointed-to data must remain + * valid and unchanged until all references to the string resource are released. + * \a *out_data must be set to non-NULL and *\a out_length must be set to greater than zero, + * since the conditions above imply that there is at least one byte available from \a offset. + */ +typedef void (*NCDVal_ComposedString_func_getptr) (void *user, size_t offset, const char **out_data, size_t *out_length); + +/** + * Structure representing a string resource used by ComposedString's, + * to simplify {@link NCDVal_NewComposedString} and {@link NCDVal_ComposedStringResource}. + */ +typedef struct { + NCDVal_ComposedString_func_getptr func_getptr; + void *user; + BRefTarget *ref_target; +} NCDValComposedStringResource; + +/** + * Returns a cstring referencing a range within a {@link NCDValComposedStringResource}. + * \a offset and \a length specify the range within the resource which the returned + * cstring will reference. To reference the contents of a ComposedString, use: + * - resource = NCDVal_ComposedStringResource(composedstring), + * - offset = NCDVal_ComposedStringOffset(composedstring), + * - length = NCDVal_StringLength(composedstring). + * + * The returned cstring is valid as long as the resource is not released. Note that + * a reference to resource.ref_target may need to be taken to ensure the resource + * is not released while it is being referenced by the returned cstring (unless + * resource.ref_target is NULL). + */ +b_cstring NCDValComposedStringResource_Cstring (NCDValComposedStringResource resource, size_t offset, size_t length); + +/** + * Builds a new ComposedString from a string resource. + * A reference to the underlying string resource via the {@link BRefTarget} object + * specified in 'resource.ref_target'. + * + * A ComposedString is a kind of String with an abstract representation exposed via the + * {@link NCDVal_ComposedString_func_getptr} callback. + */ +NCDValRef NCDVal_NewComposedString (NCDValMem *mem, NCDValComposedStringResource resource, size_t offset, size_t length); + +/** + * Returns a pointer to the data of a ContinuousString. + * WARNING: the string data may not be null-terminated. To get a null-terminated + * version, use {@link NCDVal_StringNullTerminate}. + * The value reference must point to a ContinuousString. + */ +const char * NCDVal_StringData (NCDValRef contstring); + +/** + * Returns the length of a String. + * The value reference must point to a String. + */ +size_t NCDVal_StringLength (NCDValRef string); + +/** + * Returns a {@link b_cstring} interface to the given string value. + * The returned cstring is valid as long as the memory object exists. + * However, if the memory object is moved or copied, the cstring is + * invalid in the new or moved (respectively) memory object. + */ +b_cstring NCDVal_StringCstring (NCDValRef string); + +/** + * Produces a null-terminated continuous version of a String. On success, the result is + * stored into an {@link NCDValNullTermString} structure, and the null-terminated + * string is available via its 'data' member. This function may either simply pass + * through the data pointer (if the string is known to be continuous and null-terminated) or + * produce a null-terminated dynamically allocated copy. + * On success, {@link NCDValNullTermString_Free} should be called to release any allocated + * memory when the null-terminated string is no longer needed. This must be called before + * the memory object is freed, because it may point to data inside the memory object. + * It is guaranteed that *out is not modified on failure. + * Returns 1 on success and 0 on failure. + */ +int NCDVal_StringNullTerminate (NCDValRef string, NCDValNullTermString *out) WARN_UNUSED; + +/** + * Returns a dummy {@link NCDValNullTermString} which can be freed using + * {@link NCDValNullTermString_Free}, but need not be. + */ +NCDValNullTermString NCDValNullTermString_NewDummy (void); + +/** + * Releases any memory which was dynamically allocated by {@link NCDVal_StringNullTerminate} + * to null-terminate a string. + */ +void NCDValNullTermString_Free (NCDValNullTermString *o); + +/** + * Produces a continuous version of a String. On success, the result is stored into an + * {@link NCDValContString} structure, and the continuous string is available via its + * 'data' member. This function may either simply pass through the data pointer (if the + * string is known to be continuous) or produce a continuous dynamically allocated copy. + * On success, {@link NCDValContString_Free} should be called to release any allocated + * memory when the continuous string is no longer needed. This must be called before + * the memory object is freed, because it may point to data inside the memory object. + * It is guaranteed that *out is not modified on failure. + * Returns 1 on success and 0 on failure. + */ +int NCDVal_StringContinuize (NCDValRef string, NCDValContString *out) WARN_UNUSED; + +/** + * Returns a dummy {@link NCDValContString} which can be freed using + * {@link NCDValContString_Free}, but need not be. + */ +NCDValContString NCDValContString_NewDummy (void); + +/** + * Releases any memory which was dynamically allocated by {@link NCDVal_StringContinuize} + * to continuize a string. + */ +void NCDValContString_Free (NCDValContString *o); + +/** + * Returns the string ID and the string index of an IdString. + * Both the \a out_string_id and \a out_string_index pointers must be non-NULL. + */ +void NCDVal_IdStringGet (NCDValRef idstring, NCD_string_id_t *out_string_id, + NCDStringIndex **out_string_index); + +/** + * Returns the string ID of an IdString. + */ +NCD_string_id_t NCDVal_IdStringId (NCDValRef idstring); + +/** + * Returns the string index of an IdString. + */ +NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring); + +/** + * Returns the reference target of an ExternalString. This may be NULL + * if the external string is not associated with a reference target. + */ +BRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring); + +/** + * Returns the underlying string resource of a ComposedString. + */ +NCDValComposedStringResource NCDVal_ComposedStringResource (NCDValRef composedstring); + +/** + * Returns the resource offset of a ComposedString. + */ +size_t NCDVal_ComposedStringOffset (NCDValRef composedstring); + +/** + * Determines if the String has any null bytes in its contents. + */ +int NCDVal_StringHasNulls (NCDValRef string); + +/** + * Determines if the String value is equal to the given null-terminated + * string. + * The value reference must point to a String value. + */ +int NCDVal_StringEquals (NCDValRef string, const char *data); + +/** + * Determines if the String is equal to the given string represented + * by an {@link NCDStringIndex} identifier. + * NOTE: \a string_index must be equal to the string_index of every ID-string + * that exist within this memory object. + */ +int NCDVal_StringEqualsId (NCDValRef string, NCD_string_id_t string_id, + NCDStringIndex *string_index); + +/** + * Compares two String's in a manner similar to memcmp(). + * The startN and length arguments must refer to a valid region within + * stringN, i.e. startN + length <= length_of_stringN must hold. + */ +int NCDVal_StringMemCmp (NCDValRef string1, NCDValRef string2, size_t start1, size_t start2, size_t length); + +/** + * Copies a part of a String to a buffer. + * \a start and \a length must refer to a valid region within the string, + * i.e. start + length <= length_of_string must hold. + */ +void NCDVal_StringCopyOut (NCDValRef string, size_t start, size_t length, char *dst); + +/** + * Determines if a part of a String is equal to the \a length bytes in \a data. + * \a start and \a length must refer to a valid region within the string, + * i.e. start + length <= length_of_string must hold. + */ +int NCDVal_StringRegionEquals (NCDValRef string, size_t start, size_t length, const char *data); + +/** + * Determines if a value is a list value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsList (NCDValRef val); + +/** + * Builds a new list value. The 'maxcount' argument specifies how + * many element slots to preallocate. Not more than that many + * elements may be appended to the list using {@link NCDVal_ListAppend}. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + */ +NCDValRef NCDVal_NewList (NCDValMem *mem, size_t maxcount); + +/** + * Appends a value to to the list value. + * The 'list' reference must point to a list value, and the + * 'elem' reference must be non-invalid and point to a value within + * the same memory object as the list. + * Inserting a value into a list does not in any way change it; + * internally, the list only points to it. + * You must not modify the element after it has been inserted into the + * list. + * Returns 1 on success and 0 on failure (depth limit exceeded). + */ +int NCDVal_ListAppend (NCDValRef list, NCDValRef elem) WARN_UNUSED; + +/** + * Returns the number of elements in a list value, i.e. the number + * of times {@link NCDVal_ListAppend} was called. + * The 'list' reference must point to a list value. + */ +size_t NCDVal_ListCount (NCDValRef list); + +/** + * Returns the maximum number of elements a list value may contain, + * i.e. the 'maxcount' argument to {@link NCDVal_NewList}. + * The 'list' reference must point to a list value. + */ +size_t NCDVal_ListMaxCount (NCDValRef list); + +/** + * Returns a reference to the value at the given position 'pos' in a list, + * starting with zero. + * The 'list' reference must point to a list value. + * The position 'pos' must refer to an existing element, i.e. + * pos < NCDVal_ListCount(). + */ +NCDValRef NCDVal_ListGet (NCDValRef list, size_t pos); + +/** + * Returns references to elements within a list by writing them + * via (NCDValRef *) variable arguments. + * If 'num' == NCDVal_ListCount(), succeeds, returing 1 and writing 'num' + * references, as mentioned. + * If 'num' != NCDVal_ListCount(), fails, returning 0, without writing any + * references + */ +int NCDVal_ListRead (NCDValRef list, int num, ...); + +/** + * Like {@link NCDVal_ListRead}, but the list can contain more than 'num' + * elements. + */ +int NCDVal_ListReadHead (NCDValRef list, int num, ...); + +/** + * Determines if a value is a map value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsMap (NCDValRef val); + +/** + * Builds a new map value. The 'maxcount' argument specifies how + * many entry slots to preallocate. Not more than that many + * entries may be inserted to the map using {@link NCDVal_MapInsert}. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + */ +NCDValRef NCDVal_NewMap (NCDValMem *mem, size_t maxcount); + +/** + * Inserts an entry to the map value. + * The 'map' reference must point to a map value, and the + * 'key' and 'val' references must be non-invalid and point to values within + * the same memory object as the map. + * Inserting an entry does not in any way change the 'key'and 'val'; + * internally, the map only points to it. + * You must not modify the key after inserting it into a map. This is because + * the map builds an embedded AVL tree of entries indexed by keys. + * If insertion fails due to a maximum depth limit, returns 0. + * Otherwise returns 1, and *out_inserted is set to 1 if the key did not + * yet exist and the entry was inserted, and to 0 if it did exist and the + * entry was not inserted. The 'out_inserted' pointer may be NULL, in which + * case *out_inserted is never set. + */ +int NCDVal_MapInsert (NCDValRef map, NCDValRef key, NCDValRef val, int *out_inserted) WARN_UNUSED; + +/** + * Returns the number of entries in a map value, i.e. the number + * of times {@link NCDVal_MapInsert} was called successfully. + * The 'map' reference must point to a map value. + */ +size_t NCDVal_MapCount (NCDValRef map); + +/** + * Returns the maximum number of entries a map value may contain, + * i.e. the 'maxcount' argument to {@link NCDVal_NewMap}. + * The 'map' reference must point to a map value. + */ +size_t NCDVal_MapMaxCount (NCDValRef map); + +/** + * Determines if a map entry reference is invalid. This is used in combination + * with the map iteration functions to detect the end of iteration. + */ +int NCDVal_MapElemInvalid (NCDValMapElem me); + +/** + * Returns a reference to the first entry in a map, with respect to some + * arbitrary order. + * If the map is empty, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapFirst (NCDValRef map); + +/** + * Returns a reference to the entry in a map that follows the entry referenced + * by 'me', with respect to some arbitrary order. + * The 'me' argument must be a non-invalid reference to an entry in the map. + * If 'me' is the last entry, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapNext (NCDValRef map, NCDValMapElem me); + +/** + * Like {@link NCDVal_MapFirst}, but with respect to the order defined by + * {@link NCDVal_Compare}. + * Ordered iteration is slower and should only be used when needed. + */ +NCDValMapElem NCDVal_MapOrderedFirst (NCDValRef map); + +/** + * Like {@link NCDVal_MapNext}, but with respect to the order defined by + * {@link NCDVal_Compare}. + * Ordered iteration is slower and should only be used when needed. + */ +NCDValMapElem NCDVal_MapOrderedNext (NCDValRef map, NCDValMapElem me); + +/** + * Returns a reference to the key of the map entry referenced by 'me'. + * The 'me' argument must be a non-invalid reference to an entry in the map. + */ +NCDValRef NCDVal_MapElemKey (NCDValRef map, NCDValMapElem me); + +/** + * Returns a reference to the value of the map entry referenced by 'me'. + * The 'me' argument must be a non-invalid reference to an entry in the map. + */ +NCDValRef NCDVal_MapElemVal (NCDValRef map, NCDValMapElem me); + +/** + * Looks for a key in the map. The 'key' reference must be a non-invalid + * value reference, and may point to a value in a different memory object + * than the map. + * If the key exists in the map, returns a reference to the corresponding + * map entry. + * If the key does not exist, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapFindKey (NCDValRef map, NCDValRef key); + +/** + * Retrieves the value reference to the value of the map entry whose key is a + * string value equal to the given null-terminated string. If there is no such + * entry, returns an invalid value reference. + */ +NCDValRef NCDVal_MapGetValue (NCDValRef map, const char *key_str); + +/** + * Builds a placeholder replacement program, which is a list of instructions for + * efficiently replacing placeholders in identical values in identical memory + * objects. + * To actually perform replacements, make copies of the memory object of this value + * using {@link NCDValMem_InitCopy}, then call {@link NCDValReplaceProg_Execute} + * on the copies. + * The value passed must be a valid value, and not a placeholder. + * Returns 1 on success, 0 on failure. + */ +int NCDValReplaceProg_Init (NCDValReplaceProg *o, NCDValRef val); + +/** + * Frees the placeholder replacement program. + */ +void NCDValReplaceProg_Free (NCDValReplaceProg *o); + +/** + * Callback used by {@link NCDValReplaceProg_Execute} to allow the caller to produce + * values of placeholders. + * This function should build a new value within the memory object 'mem' (which is + * the same as of the memory object where placeholders are being replaced). + * On success, it should return 1, writing a valid value reference to *out. + * On failure, it can either return 0, or return 1 but write an invalid value reference. + * This callback must not access the memory object in any other way than building + * new values in it; it must not modify any values that were already present at the + * point it was called. + */ +typedef int (*NCDVal_replace_func) (void *arg, int plid, NCDValMem *mem, NCDValRef *out); + +/** + * Executes the replacement program, replacing placeholders in a value. + * The memory object must given be identical to the memory object which was used in + * {@link NCDValReplaceProg_Init}; see {@link NCDValMem_InitCopy}. + * This will call the callback 'replace', which should build the values to replace + * the placeholders. + * Returns 1 on success and 0 on failure. On failure, the entire memory object enters + * and inconsistent state and must be freed using {@link NCDValMem_Free} before + * performing any other operation on it. + * The program is passed by value instead of pointer because this appears to be faster. + * Is is not modified in any way. + */ +int NCDValReplaceProg_Execute (NCDValReplaceProg prog, NCDValMem *mem, NCDVal_replace_func replace, void *arg); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDValCons.c b/app/src/main/jni/badvpn/ncd/NCDValCons.c new file mode 100644 index 0000000..9872a47 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValCons.c @@ -0,0 +1,283 @@ +/** + * @file NCDValCons.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "NCDValCons.h" + +#define GROWARRAY_NAME NCDValCons__Array +#define GROWARRAY_OBJECT_TYPE NCDValCons +#define GROWARRAY_ARRAY_MEMBER elems +#define GROWARRAY_CAPACITY_MEMBER elems_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +static int alloc_elem (NCDValCons *o) +{ + ASSERT(o->elems_size >= 0) + ASSERT(o->elems_size <= o->elems_capacity) + + if (o->elems_size == o->elems_capacity && !NCDValCons__Array_DoubleUp(o)) { + return -1; + } + + return o->elems_size++; +} + +static void assert_cons_val (NCDValCons *o, NCDValConsVal val) +{ +#ifndef NDEBUG + switch (val.cons_type) { + case NCDVALCONS_TYPE_COMPLETE: { + ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(o->mem, val.u.complete_ref))) + } break; + case NCDVALCONS_TYPE_INCOMPLETE_LIST: + case NCDVALCONS_TYPE_INCOMPLETE_MAP: { + ASSERT(val.u.incomplete.elems_idx >= -1) + ASSERT(val.u.incomplete.elems_idx < o->elems_size) + ASSERT(val.u.incomplete.count >= 0) + } break; + default: + ASSERT(0); + } +#endif +} + +static int complete_value (NCDValCons *o, NCDValConsVal val, NCDValSafeRef *out, int *out_error) +{ + assert_cons_val(o, val); + ASSERT(out) + ASSERT(out_error) + + switch (val.cons_type) { + case NCDVALCONS_TYPE_COMPLETE: { + *out = val.u.complete_ref; + } break; + + case NCDVALCONS_TYPE_INCOMPLETE_LIST: { + NCDValRef list = NCDVal_NewList(o->mem, val.u.incomplete.count); + if (NCDVal_IsInvalid(list)) { + goto fail_memory; + } + + int elemidx = val.u.incomplete.elems_idx; + + while (elemidx != -1) { + ASSERT(elemidx >= 0) + ASSERT(elemidx < o->elems_size) + + NCDValRef elem = NCDVal_FromSafe(o->mem, o->elems[elemidx].ref); + + if (!NCDVal_ListAppend(list, elem)) { + *out_error = NCDVALCONS_ERROR_DEPTH; + return 0; + } + + elemidx = o->elems[elemidx].next; + } + + *out = NCDVal_ToSafe(list); + } break; + + case NCDVALCONS_TYPE_INCOMPLETE_MAP: { + NCDValRef map = NCDVal_NewMap(o->mem, val.u.incomplete.count); + if (NCDVal_IsInvalid(map)) { + goto fail_memory; + } + + int keyidx = val.u.incomplete.elems_idx; + + while (keyidx != -1) { + ASSERT(keyidx >= 0) + ASSERT(keyidx < o->elems_size) + + int validx = o->elems[keyidx].next; + ASSERT(validx >= 0) + ASSERT(validx < o->elems_size) + + NCDValRef key = NCDVal_FromSafe(o->mem, o->elems[keyidx].ref); + NCDValRef value = NCDVal_FromSafe(o->mem, o->elems[validx].ref); + + int inserted; + if (!NCDVal_MapInsert(map, key, value, &inserted)) { + *out_error = NCDVALCONS_ERROR_DEPTH; + return 0; + } + if (!inserted) { + *out_error = NCDVALCONS_ERROR_DUPLICATE_KEY; + return 0; + } + + keyidx = o->elems[validx].next; + } + + *out = NCDVal_ToSafe(map); + } break; + + default: + ASSERT(0); + } + + return 1; + +fail_memory: + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; +} + +int NCDValCons_Init (NCDValCons *o, NCDValMem *mem) +{ + ASSERT(mem) + + o->mem = mem; + o->elems_size = 0; + + if (!NCDValCons__Array_Init(o, 1)) { + return 0; + } + + return 1; +} + +void NCDValCons_Free (NCDValCons *o) +{ + NCDValCons__Array_Free(o); +} + +int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error) +{ + ASSERT(out) + ASSERT(out_error) + + NCDValRef ref = NCDVal_NewStringBin(o->mem, data, len); + if (NCDVal_IsInvalid(ref)) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + out->cons_type = NCDVALCONS_TYPE_COMPLETE; + out->u.complete_ref = NCDVal_ToSafe(ref); + + return 1; +} + +void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out) +{ + ASSERT(out) + + out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_LIST; + out->u.incomplete.elems_idx = -1; + out->u.incomplete.count = 0; +} + +void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out) +{ + ASSERT(out) + + out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_MAP; + out->u.incomplete.elems_idx = -1; + out->u.incomplete.count = 0; +} + +int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error) +{ + assert_cons_val(o, *list); + ASSERT(list->cons_type == NCDVALCONS_TYPE_INCOMPLETE_LIST) + assert_cons_val(o, elem); + ASSERT(out_error) + + int elemidx = alloc_elem(o); + if (elemidx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + o->elems[elemidx].next = list->u.incomplete.elems_idx; + + if (!complete_value(o, elem, &o->elems[elemidx].ref, out_error)) { + return 0; + } + + list->u.incomplete.elems_idx = elemidx; + list->u.incomplete.count++; + + return 1; +} + +int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error) +{ + assert_cons_val(o, *map); + ASSERT(map->cons_type == NCDVALCONS_TYPE_INCOMPLETE_MAP) + assert_cons_val(o, key); + assert_cons_val(o, value); + ASSERT(out_error) + + int validx = alloc_elem(o); + if (validx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + int keyidx = alloc_elem(o); + if (keyidx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + o->elems[validx].next = map->u.incomplete.elems_idx; + o->elems[keyidx].next = validx; + + if (!complete_value(o, value, &o->elems[validx].ref, out_error)) { + return 0; + } + + if (!complete_value(o, key, &o->elems[keyidx].ref, out_error)) { + return 0; + } + + map->u.incomplete.elems_idx = keyidx; + map->u.incomplete.count++; + + return 1; +} + +int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error) +{ + assert_cons_val(o, val); + ASSERT(out) + ASSERT(out_error) + + NCDValSafeRef sref; + if (!complete_value(o, val, &sref, out_error)) { + return 0; + } + + *out = NCDVal_FromSafe(o->mem, sref); + return 1; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDValCons.h b/app/src/main/jni/badvpn/ncd/NCDValCons.h new file mode 100644 index 0000000..3b25d66 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValCons.h @@ -0,0 +1,176 @@ +/** + * @file NCDValCons.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALCONS_H +#define BADVPN_NCDVALCONS_H + +#include +#include + +#include +#include + +struct NCDValCons__temp_elem { + NCDValSafeRef ref; + int next; +}; + +/** + * Value constructor; implements a mechanism for efficiently constructing + * NCD values into {@link NCDVal} compact representation, but without + * having to know the number of list or map elements in advance. + * For the purpose of value construction, values are representing using + * {@link NCDValConsVal} objects. + */ +typedef struct { + NCDValMem *mem; + struct NCDValCons__temp_elem *elems; + int elems_size; + int elems_capacity; +} NCDValCons; + +#define NCDVALCONS_TYPE_COMPLETE 1 +#define NCDVALCONS_TYPE_INCOMPLETE_LIST 2 +#define NCDVALCONS_TYPE_INCOMPLETE_MAP 3 + +/** + * Abstract handle which represents a value during constuction via + * {@link NCDValCons}. + */ +typedef struct { + int cons_type; + union { + NCDValSafeRef complete_ref; + struct { + int elems_idx; + int count; + } incomplete; + } u; +} NCDValConsVal; + +#define NCDVALCONS_ERROR_MEMORY 1 +#define NCDVALCONS_ERROR_DUPLICATE_KEY 2 +#define NCDVALCONS_ERROR_DEPTH 3 + +/** + * Initializes a value constructor. + * + * @param o value constructor to initialize + * @param mem memory object where values will be stored into + * @return 1 on success, 0 on failure + */ +int NCDValCons_Init (NCDValCons *o, NCDValMem *mem) WARN_UNUSED; + +/** + * Frees the value constructor. This only means the constuctor does + * not exist any more; any values constructed and completed using + * {@link NCDValCons_Complete} remain in the memory object. + * + * @param o value constructor to free + */ +void NCDValCons_Free (NCDValCons *o); + +/** + * Creates a new string value with the given data. + * + * @param o value constructor + * @param data pointer to string data. This must not point into the + * memory object the value constructor is using. The data + * is copied. + * @param len length of the string + * @param out on success, *out will be set to a handle representing + * the new string + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error) WARN_UNUSED; + +/** + * Creates an empty list value. + * + * @param o value constructor + * @param out *out will be set to a handle representing the new list + */ +void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out); + +/** + * Creates an empty map value. + * + * @param o value constructor + * @param out *out will be set to a handle representing the new map + */ +void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out); + +/** + * Prepends an element to a list value. + * + * @param o value constructor + * @param list pointer to the handle representing the list. On success, + * the handle will be modified, and the old handle must not + * be used any more. + * @param elem handle representing the value to be prepended. This handle + * must not be used any more after being prepended to the list. + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error) WARN_UNUSED; + +/** + * Inserts an entry into a map value. + * + * @param o value constructor + * @param map pointer to the handle representing the map. On success, + * the handle will be modified, and the old handle must not + * be used any more. + * @param key handle representing the key of the entry. This handle + * must not be used any more after being inserted into the map. + * @param value handle representing the value of the entry. This handle + * must not be used any more after being inserted into the + * map. + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error) WARN_UNUSED; + +/** + * Completes a value represented by a {@link NCDValConsVal} handle, + * producing a {@link NCDValRef} object which refers to this value within + * the memory object. + * + * @param o value constructor + * @param val handle representing the value to be completed. After a value + * is completed, the handle must not be used any more. + * @param out on success, *out will be set to a {@link NCDValRef} object + * referencing the completed value + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDValGenerator.c b/app/src/main/jni/badvpn/ncd/NCDValGenerator.c new file mode 100644 index 0000000..3355f8b --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValGenerator.c @@ -0,0 +1,193 @@ +/** + * @file NCDValGenerator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDValGenerator.h" + +#include + +static int generate_val (NCDValRef value, ExpString *out_str) +{ + ASSERT(!NCDVal_IsInvalid(value)) + + switch (NCDVal_Type(value)) { + case NCDVAL_STRING: { + b_cstring cstr = NCDVal_StringCstring(value); + + if (!ExpString_AppendChar(out_str, '"')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + B_CSTRING_LOOP_CHARS(cstr, char_pos, ch, { + if (ch == '\0') { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02"PRIx8, (uint8_t)ch); + + if (!ExpString_Append(out_str, buf)) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + + goto next_char; + } + + if (ch == '"' || ch == '\\') { + if (!ExpString_AppendChar(out_str, '\\')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, ch)) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + next_char:; + }) + + if (!ExpString_AppendChar(out_str, '"')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(value); + + if (!ExpString_AppendChar(out_str, '{')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + for (size_t i = 0; i < count; i++) { + if (i > 0) { + if (!ExpString_Append(out_str, ", ")) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + } + + if (!generate_val(NCDVal_ListGet(value, i), out_str)) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, '}')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + case NCDVAL_MAP: { + if (!ExpString_AppendChar(out_str, '[')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + int is_first = 1; + + for (NCDValMapElem e = NCDVal_MapOrderedFirst(value); !NCDVal_MapElemInvalid(e); e = NCDVal_MapOrderedNext(value, e)) { + NCDValRef ekey = NCDVal_MapElemKey(value, e); + NCDValRef eval = NCDVal_MapElemVal(value, e); + + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + } + + if (!generate_val(ekey, out_str)) { + goto fail; + } + + if (!ExpString_AppendChar(out_str, ':')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + if (!generate_val(eval, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, ']')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +char * NCDValGenerator_Generate (NCDValRef value) +{ + ASSERT(!NCDVal_IsInvalid(value)) + + ExpString str; + if (!ExpString_Init(&str)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + if (!generate_val(value, &str)) { + goto fail1; + } + + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +int NCDValGenerator_AppendGenerate (NCDValRef value, ExpString *str) +{ + ASSERT(!NCDVal_IsInvalid(value)) + ASSERT(str) + + return generate_val(value, str); +} diff --git a/app/src/main/jni/badvpn/ncd/NCDValGenerator.h b/app/src/main/jni/badvpn/ncd/NCDValGenerator.h new file mode 100644 index 0000000..35fd991 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValGenerator.h @@ -0,0 +1,40 @@ +/** + * @file NCDValGenerator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALUEGENERATOR_H +#define BADVPN_NCDVALUEGENERATOR_H + +#include +#include +#include + +char * NCDValGenerator_Generate (NCDValRef value); +int NCDValGenerator_AppendGenerate (NCDValRef value, ExpString *str) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDValParser.c b/app/src/main/jni/badvpn/ncd/NCDValParser.c new file mode 100644 index 0000000..7cdb4da --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValParser.c @@ -0,0 +1,225 @@ +/** + * @file NCDValParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDValParser.h" + +#include + +struct token { + char *str; + size_t len; +}; + +struct value { + int have; + NCDValConsVal v; +}; + +#define ERROR_FLAG_MEMORY (1 << 0) +#define ERROR_FLAG_TOKENIZATION (1 << 1) +#define ERROR_FLAG_SYNTAX (1 << 2) +#define ERROR_FLAG_DUPLICATE_KEY (1 << 3) +#define ERROR_FLAG_DEPTH (1 << 4) + +struct parser_state { + NCDValCons cons; + NCDValRef value; + int cons_error; + int error_flags; + void *parser; +}; + +static void free_token (struct token o) +{ + if (o.str) { + free(o.str); + } +}; + +static void handle_cons_error (struct parser_state *state) +{ + switch (state->cons_error) { + case NCDVALCONS_ERROR_MEMORY: + state->error_flags |= ERROR_FLAG_MEMORY; + break; + case NCDVALCONS_ERROR_DUPLICATE_KEY: + state->error_flags |= ERROR_FLAG_DUPLICATE_KEY; + break; + case NCDVALCONS_ERROR_DEPTH: + state->error_flags |= ERROR_FLAG_DEPTH; + break; + default: + ASSERT(0); + } +} + +// rename non-static functions defined by our Lemon parser +// to avoid clashes with other Lemon parsers +#define ParseTrace ParseTrace_NCDValParser +#define ParseAlloc ParseAlloc_NCDValParser +#define ParseFree ParseFree_NCDValParser +#define Parse Parse_NCDValParser + +// include the generated Lemon parser +#include "../generated/NCDValParser_parse.c" +#include "../generated/NCDValParser_parse.h" + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + struct parser_state *state = user; + ASSERT(!state->error_flags) + + if (token == NCD_ERROR) { + state->error_flags |= ERROR_FLAG_TOKENIZATION; + goto fail; + } + + struct token minor; + minor.str = value; + minor.len = value_len; + + switch (token) { + case NCD_EOF: { + Parse(state->parser, 0, minor, state); + } break; + + case NCD_TOKEN_CURLY_OPEN: { + Parse(state->parser, CURLY_OPEN, minor, state); + } break; + + case NCD_TOKEN_CURLY_CLOSE: { + Parse(state->parser, CURLY_CLOSE, minor, state); + } break; + + case NCD_TOKEN_COMMA: { + Parse(state->parser, COMMA, minor, state); + } break; + + case NCD_TOKEN_STRING: { + Parse(state->parser, STRING, minor, state); + } break; + + case NCD_TOKEN_COLON: { + Parse(state->parser, COLON, minor, state); + } break; + + case NCD_TOKEN_BRACKET_OPEN: { + Parse(state->parser, BRACKET_OPEN, minor, state); + } break; + + case NCD_TOKEN_BRACKET_CLOSE: { + Parse(state->parser, BRACKET_CLOSE, minor, state); + } break; + + default: + state->error_flags |= ERROR_FLAG_TOKENIZATION; + free_token(minor); + goto fail; + } + + if (state->error_flags) { + goto fail; + } + + return 1; + +fail: + ASSERT(state->error_flags) + + if ((state->error_flags & ERROR_FLAG_MEMORY)) { + BLog(BLOG_ERROR, "line %zu, character %zu: memory allocation error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_TOKENIZATION)) { + BLog(BLOG_ERROR, "line %zu, character %zu: tokenization error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_SYNTAX)) { + BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_DUPLICATE_KEY)) { + BLog(BLOG_ERROR, "line %zu, character %zu: duplicate key in map error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_DEPTH)) { + BLog(BLOG_ERROR, "line %zu, character %zu: depth limit exceeded", line, line_char); + } + + return 0; +} + +int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(str_len == 0 || str) + ASSERT(mem) + ASSERT(out_value) + + int ret = 0; + + struct parser_state state; + state.value = NCDVal_NewInvalid(); + state.error_flags = 0; + + if (!NCDValCons_Init(&state.cons, mem)) { + BLog(BLOG_ERROR, "NCDValCons_Init failed"); + goto fail0; + } + + if (!(state.parser = ParseAlloc(malloc))) { + BLog(BLOG_ERROR, "ParseAlloc failed"); + goto fail1; + } + + NCDConfigTokenizer_Tokenize((char *)str, str_len, tokenizer_output, &state); + + ParseFree(state.parser, free); + + if (state.error_flags) { + goto fail1; + } + + ASSERT(!NCDVal_IsInvalid(state.value)) + + *out_value = state.value; + ret = 1; + +fail1: + NCDValCons_Free(&state.cons); +fail0: + return ret; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDValParser.h b/app/src/main/jni/badvpn/ncd/NCDValParser.h new file mode 100644 index 0000000..57d37b0 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValParser.h @@ -0,0 +1,50 @@ +/** + * @file NCDValParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALUEPARSER_H +#define BADVPN_NCDVALUEPARSER_H + +#include + +#include +#include + +/** + * Parses an NCD value string into {@link NCDVal} compact representation. + * + * @param str pointer to the string to be parsed + * @param str_len length of the string in bytes + * @param mem value memory object which the result will be stored in + * @param out_value on success, the value reference of the result will be + * written here + * @return 1 on success, 0 on failure + */ +int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/NCDValParser_parse.y b/app/src/main/jni/badvpn/ncd/NCDValParser_parse.y new file mode 100644 index 0000000..ced123d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDValParser_parse.y @@ -0,0 +1,202 @@ +/** + * @file NCDConfigParser_parse.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// argument for passing state to parser hooks +%extra_argument { struct parser_state *parser_out } + +// type of structure representing tokens +%token_type { struct token } + +// token destructor frees extra memory allocated for tokens +%token_destructor { free_token($$); } + +// types of nonterminals +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } + +// mention parser_out in some destructor to and avoid unused variable warning +%destructor list_contents { (void)parser_out; } + +// try to dynamically grow the parse stack +%stack_size 0 + +// on syntax error, set the corresponding error flag +%syntax_error { + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= value(A). { + if (!A.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &R.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + R.have = 1; + goto doneL; + +failL0: + R.have = 0; +doneL:; +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + R.have = 1; + R.v = N.v; + goto doneM; + +failM0: + R.have = 0; +doneM:; +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + NCDValCons_NewList(&parser_out->cons, &R.v); + R.have = 1; +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &R.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + R.have = 1; + goto doneS; + +failS0: + R.have = 0; +doneS:; +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + R.have = 1; + R.v = N.v; + goto doneT; + +failT0: + R.have = 0; +doneT:; +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + NCDValCons_NewMap(&parser_out->cons, &R.v); + R.have = 1; +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; +doneU:; + free_token(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} diff --git a/app/src/main/jni/badvpn/ncd/NCDVal_maptree.h b/app/src/main/jni/badvpn/ncd/NCDVal_maptree.h new file mode 100644 index 0000000..d5b9c0c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/NCDVal_maptree.h @@ -0,0 +1,15 @@ +#define CAVL_PARAM_NAME NCDVal__MapTree +#define CAVL_PARAM_FEATURE_COUNTS 0 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS 0 +#define CAVL_PARAM_TYPE_ENTRY NCDVal__maptree_entry +#define CAVL_PARAM_TYPE_LINK NCDVal__idx +#define CAVL_PARAM_TYPE_KEY NCDValRef +#define CAVL_PARAM_TYPE_ARG NCDVal__maptree_arg +#define CAVL_PARAM_VALUE_NULL ((NCDVal__idx)-1) +#define CAVL_PARAM_FUN_DEREF(arg, link) ((struct NCDVal__mapelem *)NCDValMem__BufAt((arg), (link))) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, node1, node2) NCDVal_Compare(NCDVal__Ref((arg), (node1).ptr->key_idx), NCDVal__Ref((arg), (node2).ptr->key_idx)) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, node2) NCDVal_Compare((key1), NCDVal__Ref((arg), (node2).ptr->key_idx)) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent diff --git a/app/src/main/jni/badvpn/ncd/README b/app/src/main/jni/badvpn/ncd/README new file mode 100644 index 0000000..05d638f --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/README @@ -0,0 +1,386 @@ +# This file contains some examples of using NCD, the Network Configuration Daemon. +# +# A short introduction to NCD follows. +# +# NCD is a general-purpose system configuration system, operated with a unique programming language. +# The configuration consists of one or more so-called processes that can be considered executing in +# parallel. Further, each process consists of one or more statements, representing the individual +# actions. Statements are implemented as modules built into NCD. +# +# Inside a process, statements can be considered "executed" one after another. That is, when NCD +# starts up, it initializes the first statement, putting it in the DOWN state. When the statement +# reports having transitioned into the UP state, it initializes the next statement in the DOWN state, +# and so on. +# +# However, execution can go in the other direction too. A statement in the UP state can, at any time, +# report having transitioned into the DOWN state. At this point, any statements after that one will +# automatically be de-initialized. The de-initiazation is done from the bottom up. First the last +# initialized statement after the problematic statement is requested to terminate and enters the +# DYING state. After it terminates, its preceding statement enters the DYING state, and so on, until +# all statements following the problematic statement have been de-initiazed. +# +# The backward-execution is the key feature of NCD, and is particularly well suited for programming +# system configurations. Read on to see why. +# +# Statements in NCD can be divided into two categories: +# - Statements that configure something. These statements transition into the UP state "immediately". +# On de-initialization, such statements perform the reverse operation of what they did when initialized. +# Imaginary example: a statement that turn a light on intialization, and turns if off on de-initialization. +# - Statements that wait for something. These statements may remain in the DOWN state indefinitely. +# They enter the UP state when the waited-for condition is satisfied, and also go back into the DOWN +# state when it is no longer satisfied. +# Imaginary example: a statement that is UP when a switch is turned on, and DOWN when it is turned off. +# +# Using the two example statements, we can constuct a process that controls the light based on the switch: +# (these are not really implemented in NCD :) +# +# process light { +# wait_switch(); +# turn_light(); +# } +# +# When the switch is turned on, wait_switch() will transition to UP, initializing turn_light(), turning the +# light on. When the switch is turned off, wait_switch() will transition to DOWN, causing the de-initialization +# of turn_light(), turning the light off. +# We can add another turn_light() at the end to make the switch control two lights. +# +# A more complex example: We have a christmas three with lights on it. There are multiple "regular" lights, +# controlled with switches, and a special "top" light. The regular lights take a long time to turn on, and +# each takes a different, unpredictable time. We want the top light to be turned on if and only if all the regular +# lights are completely on. +# +# This problem can easily be solved using dependencies. NCD has built-in support for dependencies, provided +# in the form of provide() and depend() statements. A depend() statement is DOWN when its corresponding +# provide() statement is not initialized, and UP when it is. When a provide() is requested to de-initialize, it +# transitions the depend() statements back into the DOWN state, and, before actually dying, waits for any +# statements following them to de-initialize. +# +# The christmas three problem can then be programmed as follows: +# +# process light1 { +# wait_switch1(); +# turn_light1(); +# provide("L1"); +# } +# +# process light2 { +# wait_switch2(); +# turn_light2(); +# provide("L2"); +# } +# +# process top_light { +# depend("L1"); +# depend("L2"); +# turn_top_light(); +# } +# +# Follow some real examples of network configuration using NCD. +# For a list of implemented statements and their descriptions, take a look at the BadVPN source code, in +# the ncd/modules/ folder. +# + +# +# Network card using DHCP. +# + +process lan { + # Make the interface name a variable so we can refer to it. + # The NCD language has no notion of assigning a variable. Instead variables are + # provided by statements preceding the statement where they are used. + # The built-in var() statement can be used to make an alias. + var("eth0") dev; + + # Wait for the network card to appear, set it up and wait for the cable to be + # plugged it. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Start DHCP. + net.ipv4.dhcp(dev) dhcp; + + # DHCP has obtained an address. + # Because net.ipv4.dhcp does no checks of the IP address, as a safety measure, do not proceed + # if the address is local. + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + + # Assign the obtained address to the interface. + net.ipv4.addr(dev, dhcp.addr, dhcp.prefix); + + # Add a default route. + # + net.ipv4.route("0.0.0.0", "0", dhcp.gateway, "20", dev); + + # Add DNS servers, as provided by DHCP. + # "20" is the priority of the servers. When applying DNS servers, NCD collects the servers + # from all active net.dns() statements, sorts them by priority ascending (stable), and writes + # them to /etc/resolv.conf, overwriting anything that was previously there. + net.dns(dhcp.dns_servers, "20"); +} + +# +# Network card with static configuration. +# + +process lan2 { + # Make the interface name a variable so we can refer to it. + var("eth1") dev; + + # Wait for the network card to appear, set it up and wait for the cable to be + # plugged it. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Assign an IP address. + # "24" is prefix length, i.e. subnet mask 255.255.255.0 + net.ipv4.addr(dev, "192.168.62.3", "24"); + + # Add a default route. + net.ipv4.route("0.0.0.0", "0", "192.168.62.3", "20", dev); + + # Build a list of DNS servers. + # The NCD language does not support "expressions" - statement arguments must be + # constant strings or variables referring to preceding statements. + # A list can be constructed using the built-in list() statement. + list("192.168.62.5", "192.168.62.6") dns_servers; + + # Add the DNS servers. + net.dns(dns_servers, "20"); +} + +# +# Wireless network interface using wpa_supplicant. +# + +process WLAN { + # Set device. + var("wlan0") dev; + + # Wait for device and rfkill switch. + net.backend.waitdevice(dev); + net.backend.rfkill("wlan", dev); + + # Start wpa_supplicant on this interface, using configuration in /etc/wpa_supplicant/all.conf . + list() args; + net.backend.wpa_supplicant(dev, "/etc/wpa_supplicant/all.conf", "/usr/sbin/wpa_supplicant", args) sup; + + # wpa_supplicant tells us what network we connected to. Look below for how this can be used to + # have different configurations, "BadVPN, but configured differently based on what network we're in". + println("connected to wireless network: bssid=", sup.bssid, " ssid=", sup.ssid); + + # Wireless connection successful, here comes network config (DHCP/static/whatever) ... +} + +# +# A BadVPN VPN interface for access to the virtual network (only). +# + +process lan { + ... (something like above) ... + + # Alias our IP address for easy access from the "vpn" process (or, for a static address, alias + # it before assigning it, and assign it using the alias). + var(dhcp.addr) ipaddr; + + # Allow VPN to start at this point. + # (and require it to stop before deconfiguring the interface if e.g. the cable is plugged out) + provide("LAN"); +} + +process vpn { + # Need the local interface to be working in order start VPN. + depend("LAN") landep; + + # Choose the name of the network interface. + var("tap3") dev; + + # Construct command line arguments for badvpn-client. Adapt according to your setup. + # "--tapdev" will be provided automatically. + + # Alias the port number that the VPN process will bind to. + var("6000") port; + + # Construct dynamic parts of command line options. + # The VPN client program needs to know some IP addresses in order to tell other peers where to connect to. + # Obtain this informations from variables in the "lan" process through the depend() statement. + + # Construct the local address (addr + port). + concat(landep.ipaddr, ":", port) local_addr_arg; + + # Construct the Internet address (assuming we are behind a NAT). + # Need to know the NAT's external address here. But we could queried it somehow. + # That is if we have preconfigured the NAT router to forward ports. But we could implement a statement + # that obtains the mappings dynamically with UPnP! + concat("1.2.3.4", ":", port) internet_addr_arg; + + # Finally construct the complete arguments, using the above address arguments. + list( + "--logger", "syslog", "--syslog-ident", "badvpn", + "--server-addr", "badvpn.example.com:7000", + "--ssl", "--nssdb", "sql:/home/badvpn/nssdb", "--client-cert-name", "peer-someone", + "--transport-mode", "udp", "--encryption-mode", "blowfish", "--hash-mode", "md5", "--otp", "blowfish", "3000", "2000", + "--scope", "mylan", "--scope", "internet", + "--bind-addr", "0.0.0.0:6000", "--num-ports", "20", + "--ext-addr", local_addr_arg, "mylan", + "--ext-addr", internet_addr_arg, "internet" + ) args; + + # Start the BadVPN backend. + # "badvpn" is the user account which the VPN client will run as. + # If you use SSL, the NSS database must be accessible to this user. + net.backend.badvpn(dev, "badvpn", "/usr/bin/badvpn-client-26", args); + + # Assign an IP address to the VPN interface. + # (we could easily use DHCP here!) + net.ipv4.addr(dev, "10.0.0.1", "24"); +} + +# +# BadVPN, but configured differently based on what network we're in. +# The network is identified based on the IP address we were assigned by DHCP. +# The different configuration provide specific arguents to badvpn-client. +# + +process lan { + ... (interface config stuff using DHCP, see above) ... + ... (the 'ipaddr' variable holds the local IP address) ... + + # Match the address to various known networks. + ip_in_network(ipaddr, "192.168.4.0", "24") is_lan1; + ip_in_network(ipaddr, "192.168.7.0", "24") is_lan2; + + # Allow VPN to start at this point. + provide("LAN"); +} + +process vpn { + ... + + # Construct common arguments here ... + list( ... ) common_args; + + # Choose appropriate configuration by waking up the configuration processes + # and waiting for one to complete. + provide("VPN_CONF_START"); + depend("VPN_CONF_END") config; + + # Concatenate common and configuration-specific arguments. + concatlist(common_args, config.args) args; + + ... +} + +process vpn_config_lan1 { + depend("VPN_CONF_START") dep; + + # Proceed only if we're in lan1. + if(dep.landep.is_lan1); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +process vpn_config_lan2 { + depend("VPN_CONF_START") dep; + + # Proceed only if we're in lan2. + if(dep.landep.is_lan2); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +process vpn_config_inet { + depend("VPN_CONF_START") dep; + + # Proceed only if we're not in any known network. + ifnot(dep.landep.is_lan1); + ifnot(dep.landep.is_lan2); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +# +# Two wired network interfaces (eth0, eth1), both of which may be used for Internet access. +# When both are working, give priority to eth1 (e.g. if eth0 is up, but later eth1 also comes +# up, the configuration will be changed to use eth1 for Internet access). +# + +process eth0 { + # Set device. + var("eth0") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + multiprovide("NET-eth0"); +} + +process eth1 { + # Set device. + var("eth1") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Static configuration. + var("192.168.111.116") addr; + var("24") addr_prefix; + var("192.168.111.1") gateway; + list("192.168.111.14", "193.2.1.66") dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + multiprovide("NET-eth1"); +} + +process NETCONF { + # Wait for some network connection. Prefer eth1 by putting it in front of eth0. + list("NET-eth1", "NET-eth0") pnames; + multidepend(pnames) ifdep; + + # Alias device values. + var(ifdep.dev) dev; + var(ifdep.addr) addr; + var(ifdep.addr_prefix) addr_prefix; + var(ifdep.gateway) gateway; + var(ifdep.dns_servers) dns_servers; + + # Add default route. + net.ipv4.route("0.0.0.0", "0", gateway, "20", dev); + + # Configure DNS servers. + net.dns(dns_servers, "20"); +} diff --git a/app/src/main/jni/badvpn/ncd/emncd.c b/app/src/main/jni/badvpn/ncd/emncd.c new file mode 100644 index 0000000..db41968 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/emncd.c @@ -0,0 +1,137 @@ +/** + * @file emncd.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static BReactor reactor; +static int running; +static NCDInterpreter interpreter; + +static void interpreter_handler_finished (void *user, int exit_code) +{ + ASSERT(running) + + fprintf(stderr, "--- interpreter terminated ---\n"); + + NCDInterpreter_Free(&interpreter); + + running = 0; +} + +__attribute__((used)) +int main () +{ + BLog_InitStderr(); + + fprintf(stderr, "--- initializing emncd version "GLOBAL_VERSION" ---\n"); + + BTime_Init(); + + BReactor_EmscriptenInit(&reactor); + + running = 0; + + return 0; +} + +__attribute__((used)) +void emncd_start (const char *program_text, int loglevel) +{ + ASSERT(program_text); + ASSERT(loglevel >= 0); + ASSERT(loglevel <= BLOG_DEBUG); + + if (running) { + fprintf(stderr, "--- cannot start, interpreter is already running! ---\n"); + return; + } + + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + BLog_SetChannelLoglevel(i, loglevel); + } + + // parse program + NCDProgram program; + if (!NCDConfigParser_Parse((char *)program_text ,strlen(program_text), &program)) { + fprintf(stderr, "--- error: failed to parse the program ---\n"); + return; + } + + // include commands are not implemented currently + if (NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE) || NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE_GUARD)) { + fprintf(stderr, "--- error: include mechanism is not supported in emncd ---\n"); + NCDProgram_Free(&program); + return; + } + + fprintf(stderr, "--- starting interpreter ---\n"); + + struct NCDInterpreter_params params; + params.handler_finished = interpreter_handler_finished; + params.user = NULL; + params.retry_time = 5000; + params.extra_args = NULL; + params.num_extra_args = 0; + params.reactor = &reactor; + + if (!NCDInterpreter_Init(&interpreter, program, params)) { + fprintf(stderr, "--- failed to initialize the interpreter ---\n"); + return; + } + + running = 1; + + BReactor_EmscriptenSync(&reactor); +} + +__attribute__((used)) +void emncd_stop (void) +{ + if (!running) { + fprintf(stderr, "--- cannot request termination, interpreter is not running! ---\n"); + return; + } + + fprintf(stderr, "--- requesting interpreter termination ---\n"); + + NCDInterpreter_RequestShutdown(&interpreter, 0); + + BReactor_EmscriptenSync(&reactor); +} diff --git a/app/src/main/jni/badvpn/ncd/emncd.html b/app/src/main/jni/badvpn/ncd/emncd.html new file mode 100644 index 0000000..befc070 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/emncd.html @@ -0,0 +1,320 @@ + + + +NCD in Javascript + + + + + + + + +This is a quick port of my NCD programming language +to Javascript using the Emscripten compiler. +
+ +Please open the Javascript console so you can see the output (Chrome: CTRL+Shift+J. Firefox: CTRL+Shift+K). +
+ + + + + + + + + + + + + +Examples: + + + + + + +
+ + +
+ +Loglevel: + None + Error + Warning + Notice + Info + Debug
+ + + +
+Note: if you get the interpreter stuck and unable to terminate, just refresh the page + + + diff --git a/app/src/main/jni/badvpn/ncd/examples/dbus_start.ncd b/app/src/main/jni/badvpn/ncd/examples/dbus_start.ncd new file mode 100644 index 0000000..832b359 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/dbus_start.ncd @@ -0,0 +1,82 @@ +process main { + call("dbus_start", {{"--session", "--nopidfile"}}) dbus_start; + + println("Got dbus: ", dbus_start.dbus_address); + rprintln("Lost dbus"); +} + +template dbus_start { + alias("_arg0") dbus_args; + + var("false") retrying; + + backtrack_point() retry_point; + If (retrying) { + println("dbus_start: retrying in one second"); + sleep("1000", "0"); + }; + + retrying->set("true"); + + listfrom({"/usr/bin/dbus-daemon"}, dbus_args, {"--print-address"}) dbus_command; + + sys.start_process(dbus_command, "r") proc; + If (proc.is_error) { + println("dbus_start: error starting process"); + retry_point->go(); + }; + + var("") dbus_address; + blocker() finished_blocker; + + process_manager() mgr; + mgr->start("dbus_start__waiter", {}); + mgr->start("dbus_start__reader", {}); + + finished_blocker->use(); +} + +template dbus_start__waiter { + alias("_caller.proc") proc; + alias("_caller.retry_point") retry_point; + + proc->wait(); + println("dbus_start: process terminated"); + retry_point->go(); +} + +template dbus_start__reader { + alias("_caller.proc") proc; + alias("_caller.retry_point") retry_point; + alias("_caller.dbus_address") dbus_address; + alias("_caller.finished_blocker") finished_blocker; + + proc->read_pipe() rpipe; + If (rpipe.is_error) { + println("dbus_start: read pipe error"); + retry_point->go(); + }; + + value("") buffer; + + backtrack_point() read_point; + rpipe->read() data; + + If (data.not_eof) { + buffer->append(data); + + explode("\n", buffer, "2") exp; + value(exp) exp; + + num_greater(exp.length, "1") found; + If (found) { + exp->get("0") address; + dbus_address->set(address); + finished_blocker->up(); + }; + + read_point->go(); + }; + + println("dbus_start: read pipe eof"); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/dhcpd.conf.template b/app/src/main/jni/badvpn/ncd/examples/dhcpd.conf.template new file mode 100644 index 0000000..9ce5ad2 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/dhcpd.conf.template @@ -0,0 +1,11 @@ +default-lease-time 600; +max-lease-time 7200; +log-facility local7; +ddns-update-style none; +local-address ; + +subnet netmask { + range ; + option routers ; + option domain-name-servers ; +} diff --git a/app/src/main/jni/badvpn/ncd/examples/directory_updater.ncd b/app/src/main/jni/badvpn/ncd/examples/directory_updater.ncd new file mode 100644 index 0000000..cb546ff --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/directory_updater.ncd @@ -0,0 +1,72 @@ +# +# Watches a directory and runs an update program whenever something +# changes. This is race-free, and if the directory changes while the +# update is running, it will be done again. +# +# NOTE: the update should not modify the directory. If it does, the +# the result will be endless and constant updating. +# + +process watcher { + # Blocker object used to trigger an update. + blocker() blk; + + # State: updating - if updater script is running + # updating_dirty - if something changed while + # script was running + var("false") updating; + var("false") updating_dirty; + + # Start update process. + spawn("updater", {}); + + # Wait for directory event. + # CHANGE THIS + sys.watch_directory("/home/ambro") watcher; + + # Print event details (e.g. "added somefile"). + println(watcher.filename, " ", watcher.event_type); + + # If updating is in progress, mark dirty. + If (updating) { + updating_dirty->set("true"); + }; + + # Request update. This makes use() proceed forward. + blk->up(); + + # Wait for next event (execution moves up). + watcher->nextevent(); +} + +template updater { + # Wait for update request. + _caller.blk->use(); + + # Wait some time. + sleep("1000", "0"); + + # We're about to start update script - set updating + # variable and mark as non dirty. + _caller.updating_dirty->set("false"); + _caller.updating->set("true"); + + println("Update started"); + + # CHANGE THIS + sleep("3000", "0"); + #runonce({"/bin/echo", "Updater speaking"}, {"keep_stdout", "keep_stderr"}); + + println("Update finished"); + + # No longer running update script. + _caller.updating->set("false"); + + # If something changed while script was running, restart + # update; else wait for next update request. + If (_caller.updating_dirty) { + _caller.blk->downup(); + } else { + _caller.blk->down(); + }; +} diff --git a/app/src/main/jni/badvpn/ncd/examples/events.ncd b/app/src/main/jni/badvpn/ncd/examples/events.ncd new file mode 100644 index 0000000..9fe80a8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/events.ncd @@ -0,0 +1,101 @@ +# +# NCD input event handling example program. +# +# This program responds to volume key presses by synchronously calling an external +# script for muting and adjusting volume, and responds to power button presses by +# suspending using pm-suspend. +# +# It uses process_manager() and sys.watch_input() to dynamically create and remove +# processes that deal with specific input devices. The individual input device processes +# then use sys.evdev() to handle input events from their input device. +# + +process events_main { + # Volume control script, called with argument "up", "down" or "mute". + var("/usr/local/bin/volumekey") volume_script; + + # Suspend command. + list("/usr/sbin/pm-suspend") suspend_cmd; + + provide("GLOBAL"); +} + +process events_watcher { + depend("GLOBAL"); + + # Create process manager. + process_manager() manager; + + # Wait for input device events. + sys.watch_input("event") watcher; + + # Dispatch. + concat("events_watcher_", watcher.event_type) func; + call(func, {}); + + # Next event. + watcher->nextevent(); +} + +template events_watcher_added { + # Start event handling process for this device. + _caller.manager->start(_caller.watcher.devname, "events_input_device", {_caller.watcher.devname}); +} + +template events_watcher_removed { + # Stop event handling process for this device. + _caller.manager->stop(_caller.watcher.devname); +} + +template events_input_device { + # Alias arguments. + var(_arg0) dev; + + # Get global. + depend("GLOBAL") gdep; + + # Wait for input events. + sys.evdev(dev) evdev; + + # Query event details. + strcmp(evdev.code, "KEY_MUTE") is_mute; + strcmp(evdev.code, "KEY_VOLUMEUP") is_vup; + strcmp(evdev.code, "KEY_VOLUMEDOWN") is_vdown; + strcmp(evdev.code, "KEY_POWER") is_power; + strcmp(evdev.value, "1") is_pressed; + + # Compute where to dispatch the event. + and(is_mute, is_pressed) dispatch_mute; + and(is_vup, is_pressed) dispatch_vup; + and(is_vdown, is_pressed) dispatch_vdown; + and(is_power, is_pressed) dispatch_power; + + # Dispatch event. + choose({ + {dispatch_mute, "events_input_event_mute"}, + {dispatch_vup, "events_input_event_vup"}, + {dispatch_vdown, "events_input_event_vdown"}, + {dispatch_power, "events_input_event_power"}}, + "" + ) func; + call(func, {}); + + # Next event. + evdev->nextevent(); +} + +template events_input_event_mute { + runonce({_caller.gdep.volume_script, "mute"}); +} + +template events_input_event_vup { + runonce({_caller.gdep.volume_script, "up"}); +} + +template events_input_event_vdown { + runonce({_caller.gdep.volume_script, "down"}); +} + +template events_input_event_power { + runonce(_caller.gdep.suspend_cmd); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/igmpproxy.conf.template b/app/src/main/jni/badvpn/ncd/examples/igmpproxy.conf.template new file mode 100644 index 0000000..9e5a83c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/igmpproxy.conf.template @@ -0,0 +1,10 @@ +quickleave + +phyint upstream ratelimit 0 threshold 1 + altnet 89.143.8.0/24 + altnet 95.176.0.0/16 + +phyint downstream ratelimit 0 threshold 1 + +# A lot of these will be appended by make_igmpproxy_config: +# phyint eth0 disabled diff --git a/app/src/main/jni/badvpn/ncd/examples/make_dhcp_config.ncd b/app/src/main/jni/badvpn/ncd/examples/make_dhcp_config.ncd new file mode 100644 index 0000000..201a052 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/make_dhcp_config.ncd @@ -0,0 +1,27 @@ +process main { + call("make_dhcp_config", {"192.168.1.1", "24", "192.168.1.100", "192.168.1.199", {"192.168.1.1"}, {"192.168.1.1", "4.4.4.4"}, "dhcpd.conf.template", "dhcpd.conf"}) config; + exit("0"); +} + +template make_dhcp_config { + alias("_arg0") addr; + alias("_arg1") prefix; + alias("_arg2") range_start; + alias("_arg3") range_end; + alias("_arg4") routers; + alias("_arg5") dns_servers; + alias("_arg6") template_file; + alias("_arg7") output_file; + + ipv4_net_from_addr_and_prefix(addr, prefix) network; + ipv4_prefix_to_mask(prefix) netmask; + implode(", ", routers) routers_str; + implode(", ", dns_servers) dns_servers_str; + + var({"", "", "", "", "", "", ""}) regex; + var({addr, network, netmask, range_start, range_end, routers_str, dns_servers_str}) replace; + + file_read(template_file) template_data; + regex_replace(template_data, regex, replace) replaced_data; + file_write(output_file, replaced_data); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/make_igmpproxy_config.ncd b/app/src/main/jni/badvpn/ncd/examples/make_igmpproxy_config.ncd new file mode 100644 index 0000000..77ca0a9 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/make_igmpproxy_config.ncd @@ -0,0 +1,53 @@ +process main { + call("make_igmpproxy_config", {"br0", "eth1", "./igmpproxy.conf.template", "./igmpproxy.conf"}); + + println("Done."); + exit("0"); +} + +template make_igmpproxy_config { + alias("_arg0") upstream_iface; + alias("_arg1") downstream_iface; + alias("_arg2") template_file; + alias("_arg3") output_file; + + # Make a list of interfaces to add as disabled (upstream and downstream will not be disabled). + var({ + "eth0", "eth1", "eth2", "eth3", "eth4", "eth5", "eth6", "eth7", "eth8", "eth9", + "wlan0", "wlan1", "wlan2", "wlan3", "wlan4", "wlan5", "wlan6", "wlan7", "wlan8", "wlan9", + "tap0", "tap1", "tap2", "tap3", "tap4", "tap5", "tap6", "tap7", "tap8", "tap9", + "br0", "br1", "br2", "br3", "br4", "br5", "br6", "br7", "br8", "br9" + }) disabled_ifaces; + + # Build replacements for template config. + var({"", ""}) regex; + var({upstream_iface, downstream_iface}) replace; + + # Read template config. + file_read(template_file) template_data; + + # Perform replacements. + regex_replace(template_data, regex, replace) replaced_data; + + # Build string value from replaced_data, to which we will append + # configuration for disabled interfaces. + value(replaced_data) output_data; + + # Build disabled strings. + Foreach (disabled_ifaces As iface) { + # Determine if the interface is disabled. + val_equal(iface, upstream_iface) is_up; + val_equal(iface, downstream_iface) is_down; + or(is_up, is_down) is_not_disabled; + not(is_not_disabled) is_disabled; + + # If it's disabled, append to the configuration file. + If (is_disabled) { + concat("phyint ", iface, " disabled\n") str; + output_data->append(str); + }; + }; + + # Write config. + file_write(output_file, output_data); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/network.ncd b/app/src/main/jni/badvpn/ncd/examples/network.ncd new file mode 100644 index 0000000..2eaeb8f --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/network.ncd @@ -0,0 +1,123 @@ +# An example NCD script for network configuration. +# +# The first three processes demonstrate different kinds of interfaces +# and configurations. They are all disabled by default. +# +# The last process waits for one of the interfaces to come up +# and sets up routes and DNS entries to use that interface for +# Internet access. +# +# Be sure to change the dependency list in the last process to name +# the interfaces you use. + +# Example wired interface with static configuration. +process wired_example_static { + if("false"); # remove/comment to enable + + # Set device. + var("eth0") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Static configuration. + var("192.168.111.116") addr; + var("24") addr_prefix; + var("192.168.111.1") gateway; + var({"192.168.111.14", "193.2.1.66"}) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# Example wired interface with DHCP configuration. +process wired_example_dhcp { + if("false"); # remove/comment to enable + + # Set device. + var("eth1") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# Example wireless interface with DHCP configuration. +# This will use the wpa_supplicant configuration file /etc/wpa_supplicant/all.conf +# which should specify the wireless networks and other options. +process wireless_example_dhcp { + if("false"); # remove/comment to enable + + # Set device. + var("wlan0") dev; + + # Wait for device and rfkill. + net.backend.waitdevice(dev); + net.backend.rfkill("wlan", dev); + + # Connect to wireless network. + net.backend.wpa_supplicant(dev, "/etc/wpa_supplicant/all.conf", "/usr/sbin/wpa_supplicant", {}); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# This process sets up routes and DNS servers for at most one of +# the working interfaces. It will change the configuration if a +# more important interface comes up while one is already up. +process NETCONF { + # Choose devices and priorities; put preferred devices to the front. + var({"NET-eth0", "NET-eth1", "NET-wlan0"}) provide_names; + + # Wait for one of the interfaces (and deinit/switch appropriately). + multidepend(provide_names) ifdep; + + # Alias device values. + var(ifdep.dev) dev; + var(ifdep.addr) addr; + var(ifdep.addr_prefix) addr_prefix; + var(ifdep.gateway) gateway; + var(ifdep.dns_servers) dns_servers; + + # Add default route. + net.ipv4.route("0.0.0.0", "0", gateway, "20", dev); + + # Configure DNS servers. + net.dns(dns_servers, "20"); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/onoff_server.ncdi b/app/src/main/jni/badvpn/ncd/examples/onoff_server.ncdi new file mode 100644 index 0000000..a380cde --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/onoff_server.ncdi @@ -0,0 +1,82 @@ +include_guard "onoff_server" + +template onoff_server_main { + alias("_arg0") socket_path; + + depend_scope() depsc; + process_manager() mgr; + + sys.request_server({"unix", socket_path}, "onoff_server__request_handler", {}); +} + +template onoff_server_onoff { + alias(_arg0) main; + alias("_arg1") onoff_id; + alias("_arg2") default_state; + + main.mgr->start(onoff_id, "onoff_server__id_proc", {onoff_id, default_state}); + main.depsc->depend({onoff_id}) id_proc; + + id_proc.blk->use(); +} + +template onoff_server__id_proc { + alias("_caller") main; + alias("_arg0") onoff_id; + alias("_arg1") default_state; + + blocker() blk; + If (default_state) { + blk->up(); + }; + main.depsc->provide(onoff_id); +} + +template onoff_server__request_handler { + alias("_caller") main; + + try("onoff_server__try_request", {}); + + _request->reply({"error", "Bad request."}); + _request->finish(); +} + +template onoff_server__try_request { + alias("_caller.main") main; + alias("_caller._request") request; + + value(request.data) data; + + val_equal(data.type, "list") a; + _try->assert(a); + + num_greater_equal(data.length, "1") a; + _try->assert(a); + + data->get("0") request_cmd; + val_equal(request_cmd, "set_onoff") is_set_onoff; + + If (is_set_onoff) { + num_equal(data.length, "3") a; + _try->assert(a); + + data->get("1") onoff_id; + data->get("2") new_state; + + main.mgr->start(onoff_id, "onoff_server__id_proc", {onoff_id, new_state}); + main.depsc->depend({onoff_id}) id_proc; + + val_equal(new_state, "true") is_on; + If (is_on) { + id_proc.blk->up(); + } Else { + id_proc.blk->down(); + }; + } + Else { + _try->assert("false"); + }; + + request->reply({"OK", "state has been set"}); + request->finish(); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/onoff_server_test.ncd b/app/src/main/jni/badvpn/ncd/examples/onoff_server_test.ncd new file mode 100644 index 0000000..7ff0b16 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/onoff_server_test.ncd @@ -0,0 +1,20 @@ +include "onoff_server.ncdi" + +process main { + call("onoff_server_main", {"/home/ambro/onoff.socket"}) onoff_server; + + process_manager() mgr; + mgr->start("service1", {}); + #mgr->start("service2", {}); +} + +template service1 { + alias("_caller") main; + + call("onoff_server_onoff", {"_caller.main.onoff_server", "ServiceId1", "true"}); + + println("service1 up"); + rprintln("service1 down"); + + # Do your stuff. +} diff --git a/app/src/main/jni/badvpn/ncd/examples/tcp_echo_client.ncd b/app/src/main/jni/badvpn/ncd/examples/tcp_echo_client.ncd new file mode 100644 index 0000000..47dd999 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/tcp_echo_client.ncd @@ -0,0 +1,35 @@ +process main { + getargs() args; + value(args) args; + + num_different(args.length, "2") bad_args; + If (bad_args) { + println("bad arguments"); + exit("1"); + }; + + args->get("0") addr_ip; + args->get("1") addr_port; + + sys.connect({"tcp", {"ipv4", addr_ip, addr_port}}) socket; + If (socket.is_error) { + println("connection error!"); + exit("1"); + }; + + println("connected"); + + socket->write("This echo client is implemented in NCD!\n\n"); + + backtrack_point() recv_point; + + socket->read() data; + If (data.not_eof) { + socket->write(data); + recv_point->go(); + }; + + println("server disconnected"); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/examples/tcp_echo_server.ncd b/app/src/main/jni/badvpn/ncd/examples/tcp_echo_server.ncd new file mode 100644 index 0000000..a3c6267 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/examples/tcp_echo_server.ncd @@ -0,0 +1,40 @@ +process main { + getargs() args; + value(args) args; + + num_different(args.length, "2") bad_args; + If (bad_args) { + println("bad arguments"); + exit("1"); + }; + + args->get("0") addr_ip; + args->get("1") addr_port; + + sys.listen({"tcp", {"ipv4", addr_ip, addr_port}}, "client_handler", {}) listener; + If (listener.is_error) { + println("failed to listen"); + exit("1"); + }; + + println("listening"); +} + +template client_handler { + to_string(_socket.client_addr) addr_str; + + println("client ", addr_str, ": connected"); + rprintln("client ", addr_str, ": disconnected"); + + _socket->write("This echo server is implemented in NCD!\n\n"); + + backtrack_point() recv_point; + + _socket->read() data; + If (data.not_eof) { + _socket->write(data); + recv_point->go(); + }; + + _socket->close(); +} diff --git a/app/src/main/jni/badvpn/ncd/extra/BEventLock.c b/app/src/main/jni/badvpn/ncd/extra/BEventLock.c new file mode 100644 index 0000000..e65bd20 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/BEventLock.c @@ -0,0 +1,146 @@ +/** + * @file BEventLock.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "BEventLock.h" + +static void exec_job_handler (BEventLock *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->jobs)) + DebugObject_Access(&o->d_obj); + + // get job + BEventLockJob *j = UPPER_OBJECT(LinkedList1_GetFirst(&o->jobs), BEventLockJob, pending_node); + ASSERT(j->pending) + + // call handler + j->handler(j->user); + return; +} + +void BEventLock_Init (BEventLock *o, BPendingGroup *pg) +{ + // init jobs list + LinkedList1_Init(&o->jobs); + + // init exec job + BPending_Init(&o->exec_job, pg, (BPending_handler)exec_job_handler, o); + + DebugObject_Init(&o->d_obj); + DebugCounter_Init(&o->pending_ctr); +} + +void BEventLock_Free (BEventLock *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->jobs)) + DebugCounter_Free(&o->pending_ctr); + DebugObject_Free(&o->d_obj); + + // free exec jobs + BPending_Free(&o->exec_job); +} + +void BEventLockJob_Init (BEventLockJob *o, BEventLock *l, BEventLock_handler handler, void *user) +{ + // init arguments + o->l = l; + o->handler = handler; + o->user = user; + + // set not pending + o->pending = 0; + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&l->pending_ctr); +} + +void BEventLockJob_Free (BEventLockJob *o) +{ + BEventLock *l = o->l; + + DebugCounter_Decrement(&l->pending_ctr); + DebugObject_Free(&o->d_obj); + + if (o->pending) { + int was_first = (&o->pending_node == LinkedList1_GetFirst(&l->jobs)); + + // remove from jobs list + LinkedList1_Remove(&l->jobs, &o->pending_node); + + // schedule/unschedule job + if (was_first) { + if (LinkedList1_IsEmpty(&l->jobs)) { + BPending_Unset(&l->exec_job); + } else { + BPending_Set(&l->exec_job); + } + } + } +} + +void BEventLockJob_Wait (BEventLockJob *o) +{ + BEventLock *l = o->l; + ASSERT(!o->pending) + + // append to jobs + LinkedList1_Append(&l->jobs, &o->pending_node); + + // set pending + o->pending = 1; + + // schedule next job if needed + if (&o->pending_node == LinkedList1_GetFirst(&l->jobs)) { + BPending_Set(&l->exec_job); + } +} + +void BEventLockJob_Release (BEventLockJob *o) +{ + BEventLock *l = o->l; + ASSERT(o->pending) + + int was_first = (&o->pending_node == LinkedList1_GetFirst(&l->jobs)); + + // remove from jobs list + LinkedList1_Remove(&l->jobs, &o->pending_node); + + // set not pending + o->pending = 0; + + // schedule/unschedule job + if (was_first) { + if (LinkedList1_IsEmpty(&l->jobs)) { + BPending_Unset(&l->exec_job); + } else { + BPending_Set(&l->exec_job); + } + } +} diff --git a/app/src/main/jni/badvpn/ncd/extra/BEventLock.h b/app/src/main/jni/badvpn/ncd/extra/BEventLock.h new file mode 100644 index 0000000..2664318 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/BEventLock.h @@ -0,0 +1,127 @@ +/** + * @file BEventLock.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A FIFO lock for events using the job queue ({@link BPending}). + */ + +#ifndef BADVPN_BEVENTLOCK_H +#define BADVPN_BEVENTLOCK_H + +#include +#include +#include +#include + +/** + * Event context handler called when the lock job has acquired the lock + * after requesting the lock with {@link BEventLockJob_Wait}. + * The object was in waiting state. + * The object enters locked state before the handler is called. + * + * @param user as in {@link BEventLockJob_Init} + */ +typedef void (*BEventLock_handler) (void *user); + +/** + * A FIFO lock for events using the job queue ({@link BPending}). + */ +typedef struct { + LinkedList1 jobs; + BPending exec_job; + DebugObject d_obj; + DebugCounter pending_ctr; +} BEventLock; + +/** + * An object that can request a {@link BEventLock} lock. + */ +typedef struct { + BEventLock *l; + BEventLock_handler handler; + void *user; + int pending; + LinkedList1Node pending_node; + DebugObject d_obj; +} BEventLockJob; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void BEventLock_Init (BEventLock *o, BPendingGroup *pg); + +/** + * Frees the object. + * There must be no {@link BEventLockJob} objects using this lock + * (regardless of their state). + * + * @param o the object + */ +void BEventLock_Free (BEventLock *o); + +/** + * Initializes the object. + * The object is initialized in idle state. + * + * @param o the object + * @param l the lock + * @param handler handler to call when the lock is aquired + * @param user value to pass to handler + */ +void BEventLockJob_Init (BEventLockJob *o, BEventLock *l, BEventLock_handler handler, void *user); + +/** + * Frees the object. + * + * @param o the object + */ +void BEventLockJob_Free (BEventLockJob *o); + +/** + * Requests the lock. + * The object must be in idle state. + * The object enters waiting state. + * + * @param o the object + */ +void BEventLockJob_Wait (BEventLockJob *o); + +/** + * Aborts the lock request or releases the lock. + * The object must be in waiting or locked state. + * The object enters idle state. + * + * @param o the object + */ +void BEventLockJob_Release (BEventLockJob *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.c b/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.c new file mode 100644 index 0000000..2af4beb --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.c @@ -0,0 +1,154 @@ +/** + * @file NCDBProcessOpts.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include "NCDBProcessOpts.h" + +int NCDBProcessOpts_Init (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel) +{ + return NCDBProcessOpts_Init2(o, opts_arg, func_unknown, func_unknown_user, i, blog_channel, NULL, NULL); +} + +int NCDBProcessOpts_Init2 (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel, + int *out_keep_stdout, int *out_keep_stderr) +{ + if (!NCDVal_IsInvalid(opts_arg) && !NCDVal_IsMap(opts_arg)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "options must be a map"); + goto fail0; + } + + o->username = NULL; + o->do_setsid = 0; + + int keep_stdout = 0; + int keep_stderr = 0; + + if (!NCDVal_IsInvalid(opts_arg)) { + for (NCDValMapElem me = NCDVal_MapFirst(opts_arg); !NCDVal_MapElemInvalid(me); me = NCDVal_MapNext(opts_arg, me)) { + NCDValRef key = NCDVal_MapElemKey(opts_arg, me); + NCDValRef val = NCDVal_MapElemVal(opts_arg, me); + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "keep_stdout")) { + keep_stdout = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "keep_stderr")) { + keep_stderr = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "do_setsid")) { + o->do_setsid = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "username")) { + if (!NCDVal_IsStringNoNulls(val)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "username must be a string without nulls"); + goto fail1; + } + b_cstring cstr = NCDVal_StringCstring(val); + o->username = b_cstring_strdup(cstr, 0, cstr.length); + if (!o->username) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "b_cstring_strdup failed"); + goto fail1; + } + } + else { + if (!func_unknown || !func_unknown(func_unknown_user, key, val)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "unknown option"); + goto fail1; + } + } + } + } + + o->nfds = 0; + if (keep_stdout) { + o->fds[o->nfds] = 1; + o->fds_map[o->nfds++] = 1; + } + if (keep_stderr) { + o->fds[o->nfds] = 2; + o->fds_map[o->nfds++] = 2; + } + o->fds[o->nfds] = -1; + + if (out_keep_stdout) { + *out_keep_stdout = keep_stdout; + } + if (out_keep_stderr) { + *out_keep_stderr = keep_stderr; + } + + return 1; + +fail1: + if (o->username) { + BFree(o->username); + } +fail0: + return 0; +} + +void NCDBProcessOpts_InitOld (NCDBProcessOpts *o, int keep_stdout, int keep_stderr, int do_setsid) +{ + o->username = NULL; + o->do_setsid = do_setsid; + + o->nfds = 0; + if (keep_stdout) { + o->fds[o->nfds] = 1; + o->fds_map[o->nfds++] = 1; + } + if (keep_stderr) { + o->fds[o->nfds] = 2; + o->fds_map[o->nfds++] = 2; + } + o->fds[o->nfds] = -1; +} + +void NCDBProcessOpts_Free (NCDBProcessOpts *o) +{ + if (o->username) { + BFree(o->username); + } +} + +struct BProcess_params NCDBProcessOpts_GetParams (NCDBProcessOpts *o) +{ + struct BProcess_params params; + + params.username = o->username; + params.fds = o->fds; + params.fds_map = o->fds_map; + params.do_setsid = o->do_setsid; + + return params; +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.h b/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.h new file mode 100644 index 0000000..9a9a79c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDBProcessOpts.h @@ -0,0 +1,54 @@ +/** + * @file NCDBProcessOpts.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BPROCESS_OPTS_H +#define NCD_BPROCESS_OPTS_H + +#include +#include +#include + +typedef struct { + char *username; + int do_setsid; + int fds[3]; + int fds_map[2]; + int nfds; +} NCDBProcessOpts; + +typedef int (*NCDBProcessOpts_func_unknown) (void *user, NCDValRef key, NCDValRef val); + +int NCDBProcessOpts_Init (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel) WARN_UNUSED; +int NCDBProcessOpts_Init2 (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel, + int *out_keep_stdout, int *out_keep_stderr) WARN_UNUSED; +void NCDBProcessOpts_InitOld (NCDBProcessOpts *o, int keep_stdout, int keep_stderr, int do_setsid); +void NCDBProcessOpts_Free (NCDBProcessOpts *o); +struct BProcess_params NCDBProcessOpts_GetParams (NCDBProcessOpts *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDBuf.c b/app/src/main/jni/badvpn/ncd/extra/NCDBuf.c new file mode 100644 index 0000000..6262652 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDBuf.c @@ -0,0 +1,123 @@ +/** + * @file NCDBuf.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "NCDBuf.h" + +#include +#include +#include + +static void ref_target_func_release (BRefTarget *ref_target) +{ + NCDBuf *o = UPPER_OBJECT(ref_target, NCDBuf, ref_target); + NCDBufStore *store = o->store; + + if (store) { + LinkedList0_Remove(&store->used_bufs_list, &o->list_node); + LinkedList0_Prepend(&store->free_bufs_list, &o->list_node); + } else { + BFree(o); + } +} + +void NCDBufStore_Init (NCDBufStore *o, size_t buf_size) +{ + o->buf_size = buf_size; + LinkedList0_Init(&o->used_bufs_list); + LinkedList0_Init(&o->free_bufs_list); + + DebugObject_Init(&o->d_obj); +} + +void NCDBufStore_Free (NCDBufStore *o) +{ + DebugObject_Free(&o->d_obj); + + LinkedList0Node *ln; + + ln = LinkedList0_GetFirst(&o->used_bufs_list); + while (ln) { + NCDBuf *buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + buf->store = NULL; + ln = LinkedList0Node_Next(ln); + } + + ln = LinkedList0_GetFirst(&o->free_bufs_list); + while (ln) { + LinkedList0Node *next_ln = LinkedList0Node_Next(ln); + NCDBuf *buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + BFree(buf); + ln = next_ln; + } +} + +size_t NCDBufStore_BufSize (NCDBufStore *o) +{ + DebugObject_Access(&o->d_obj); + + return o->buf_size; +} + +NCDBuf * NCDBufStore_GetBuf (NCDBufStore *o) +{ + DebugObject_Access(&o->d_obj); + + NCDBuf *buf; + + LinkedList0Node *ln = LinkedList0_GetFirst(&o->free_bufs_list); + if (ln) { + buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + LinkedList0_Remove(&o->free_bufs_list, &buf->list_node); + } else { + bsize_t size = bsize_add(bsize_fromsize(sizeof(NCDBuf)), bsize_fromsize(o->buf_size)); + buf = BAllocSize(size); + if (!buf) { + return NULL; + } + buf->store = o; + } + + LinkedList0_Prepend(&o->used_bufs_list, &buf->list_node); + BRefTarget_Init(&buf->ref_target, ref_target_func_release); + + return buf; +} + +BRefTarget * NCDBuf_RefTarget (NCDBuf *o) +{ + return &o->ref_target; +} + +char * NCDBuf_Data (NCDBuf *o) +{ + return o->data; +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDBuf.h b/app/src/main/jni/badvpn/ncd/extra/NCDBuf.h new file mode 100644 index 0000000..8542dcd --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDBuf.h @@ -0,0 +1,61 @@ +/** + * @file NCDBuf.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_NCDBUF_H +#define NCD_NCDBUF_H + +#include + +#include +#include +#include + +typedef struct { + size_t buf_size; + LinkedList0 used_bufs_list; + LinkedList0 free_bufs_list; + DebugObject d_obj; +} NCDBufStore; + +typedef struct { + NCDBufStore *store; + LinkedList0Node list_node; + BRefTarget ref_target; + char data[]; +} NCDBuf; + +void NCDBufStore_Init (NCDBufStore *o, size_t buf_size); +void NCDBufStore_Free (NCDBufStore *o); +size_t NCDBufStore_BufSize (NCDBufStore *o); +NCDBuf * NCDBufStore_GetBuf (NCDBufStore *o); + +BRefTarget * NCDBuf_RefTarget (NCDBuf *o); +char * NCDBuf_Data (NCDBuf *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.c b/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.c new file mode 100644 index 0000000..8b68180 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.c @@ -0,0 +1,492 @@ +/** + * @file NCDIfConfig.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "NCDIfConfig.h" + +#include + +#define IP_CMD "ip" +#define RESOLVCONF_FILE "/etc/resolv.conf" +#define RESOLVCONF_TEMP_FILE "/etc/resolv.conf-ncd-temp" +#define TUN_DEVNODE "/dev/net/tun" + +static int run_command (const char *cmd) +{ + BLog(BLOG_INFO, "run: %s", cmd); + + return system(cmd); +} + +static int write_to_file (uint8_t *data, size_t data_len, FILE *f) +{ + while (data_len > 0) { + size_t bytes = fwrite(data, 1, data_len, f); + if (bytes == 0) { + return 0; + } + data += bytes; + data_len -= bytes; + } + + return 1; +} + +int NCDIfConfig_query (const char *ifname) +{ + struct ifreq ifr; + + int flags = 0; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (!s) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFFLAGS, &ifr)) { + BLog(BLOG_ERROR, "SIOCGIFFLAGS failed"); + goto fail1; + } + + flags |= NCDIFCONFIG_FLAG_EXISTS; + + if ((ifr.ifr_flags&IFF_UP)) { + flags |= NCDIFCONFIG_FLAG_UP; + + if ((ifr.ifr_flags&IFF_RUNNING)) { + flags |= NCDIFCONFIG_FLAG_RUNNING; + } + } + +fail1: + close(s); +fail0: + return flags; +} + +int NCDIfConfig_set_up (const char *ifname) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" link set %s up", ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_set_down (const char *ifname) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" link set %s down", ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *addr = (uint8_t *)&ifaddr.addr; + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr add %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_remove_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *addr = (uint8_t *)&ifaddr.addr; + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr del %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char addr_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(ifaddr.addr, addr_str); + + char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr add %s/%d dev %s", addr_str, ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_remove_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char addr_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(ifaddr.addr, addr_str); + + char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr del %s/%d dev %s", addr_str, ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +static int route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *ifname) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *d_addr = (uint8_t *)&dest.addr; + + char gwstr[30]; + if (gateway) { + const uint8_t *g_addr = (uint8_t *)gateway; + sprintf(gwstr, " via %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, g_addr[0], g_addr[1], g_addr[2], g_addr[3]); + } else { + gwstr[0] = '\0'; + } + + char cmd[120 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" route %s %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d%s metric %d dev %s", + cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, gwstr, metric, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device) +{ + return route_cmd("add", dest, gateway, metric, device); +} + +int NCDIfConfig_remove_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device) +{ + return route_cmd("del", dest, gateway, metric, device); +} + +static int route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *ifname) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char dest_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(dest.addr, dest_str); + + char gwstr[10 + IPADDR6_PRINT_MAX]; + if (gateway) { + strcpy(gwstr, " via "); + ipaddr6_print_addr(*gateway, gwstr + strlen(gwstr)); + } else { + gwstr[0] = '\0'; + } + + char cmd[70 + IPADDR6_PRINT_MAX + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" route %s %s/%d%s metric %d dev %s", + cmdtype, dest_str, dest.prefix, gwstr, metric, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device) +{ + return route_cmd6("add", dest, gateway, metric, device); +} + +int NCDIfConfig_remove_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device) +{ + return route_cmd6("del", dest, gateway, metric, device); +} + +static int blackhole_route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, int metric) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 32) + + uint8_t *d_addr = (uint8_t *)&dest.addr; + + char cmd[120]; + sprintf(cmd, IP_CMD" route %s blackhole %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d metric %d", + cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, metric); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric) +{ + return blackhole_route_cmd("add", dest, metric); +} + +int NCDIfConfig_remove_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric) +{ + return blackhole_route_cmd("del", dest, metric); +} + +static int blackhole_route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, int metric) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 128) + + char dest_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(dest.addr, dest_str); + + char cmd[70 + IPADDR6_PRINT_MAX]; + sprintf(cmd, IP_CMD" route %s blackhole %s/%d metric %d", + cmdtype, dest_str, dest.prefix, metric); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric) +{ + return blackhole_route_cmd6("add", dest, metric); +} + +int NCDIfConfig_remove_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric) +{ + return blackhole_route_cmd6("del", dest, metric); +} + +int NCDIfConfig_set_dns_servers (uint32_t *servers, size_t num_servers) +{ + FILE *temp_file = fopen(RESOLVCONF_TEMP_FILE, "w"); + if (!temp_file) { + BLog(BLOG_ERROR, "failed to open resolvconf temp file"); + goto fail0; + } + + char line[60]; + + sprintf(line, "# generated by badvpn-ncd\n"); + if (!write_to_file((uint8_t *)line, strlen(line), temp_file)) { + BLog(BLOG_ERROR, "failed to write to resolvconf temp file"); + goto fail1; + } + + for (size_t i = 0; i < num_servers; i++) { + uint8_t *addr = (uint8_t *)&servers[i]; + sprintf(line, "nameserver %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"\n", + addr[0], addr[1], addr[2], addr[3]); + if (!write_to_file((uint8_t *)line, strlen(line), temp_file)) { + BLog(BLOG_ERROR, "failed to write to resolvconf temp file"); + goto fail1; + } + } + + if (fclose(temp_file) != 0) { + BLog(BLOG_ERROR, "failed to close resolvconf temp file"); + return 0; + } + + if (rename(RESOLVCONF_TEMP_FILE, RESOLVCONF_FILE) < 0) { + BLog(BLOG_ERROR, "failed to rename resolvconf temp file to resolvconf file"); + return 0; + } + + return 1; + +fail1: + fclose(temp_file); +fail0: + return 0; +} + +static int open_tuntap (const char *ifname, int flags) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return -1; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + BLog(BLOG_ERROR, "open tun failed"); + return -1; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = flags; + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "TUNSETIFF failed"); + close(fd); + return -1; + } + + return fd; +} + +int NCDIfConfig_make_tuntap (const char *ifname, const char *owner, int tun) +{ + // load tun module if needed + if (access(TUN_DEVNODE, F_OK) < 0) { + if (run_command("/sbin/modprobe tun") != 0) { + BLog(BLOG_ERROR, "modprobe tun failed"); + } + } + + int fd; + if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) { + goto fail0; + } + + if (owner) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail1; + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(owner, &pwd, buf, bufsize, &res); + if (!res) { + BLog(BLOG_ERROR, "getpwnam_r failed"); + free(buf); + goto fail1; + } + + int uid = pwd.pw_uid; + + free(buf); + + if (ioctl(fd, TUNSETOWNER, uid) < 0) { + BLog(BLOG_ERROR, "TUNSETOWNER failed"); + goto fail1; + } + } + + if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) { + BLog(BLOG_ERROR, "TUNSETPERSIST failed"); + goto fail1; + } + + close(fd); + + return 1; + +fail1: + close(fd); +fail0: + return 0; +} + +int NCDIfConfig_remove_tuntap (const char *ifname, int tun) +{ + int fd; + if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) { + goto fail0; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) { + BLog(BLOG_ERROR, "TUNSETPERSIST failed"); + goto fail1; + } + + close(fd); + + return 1; + +fail1: + close(fd); +fail0: + return 0; +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.h b/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.h new file mode 100644 index 0000000..dbcbf04 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDIfConfig.h @@ -0,0 +1,70 @@ +/** + * @file NCDIfConfig.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDIFCONFIG_H +#define BADVPN_NCD_NCDIFCONFIG_H + +#include + +#include +#include + +#define NCDIFCONFIG_FLAG_EXISTS (1 << 0) +#define NCDIFCONFIG_FLAG_UP (1 << 1) +#define NCDIFCONFIG_FLAG_RUNNING (1 << 2) + +int NCDIfConfig_query (const char *ifname); + +int NCDIfConfig_set_up (const char *ifname); +int NCDIfConfig_set_down (const char *ifname); + +int NCDIfConfig_add_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr); +int NCDIfConfig_remove_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr); + +int NCDIfConfig_add_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr); +int NCDIfConfig_remove_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr); + +int NCDIfConfig_add_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device); +int NCDIfConfig_remove_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device); + +int NCDIfConfig_add_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device); +int NCDIfConfig_remove_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device); + +int NCDIfConfig_add_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric); +int NCDIfConfig_remove_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric); + +int NCDIfConfig_add_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric); +int NCDIfConfig_remove_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric); + +int NCDIfConfig_set_dns_servers (uint32_t *servers, size_t num_servers); + +int NCDIfConfig_make_tuntap (const char *ifname, const char *owner, int tun); +int NCDIfConfig_remove_tuntap (const char *ifname, int tun); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.c b/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.c new file mode 100644 index 0000000..8b457ef --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.c @@ -0,0 +1,446 @@ +/** + * @file NCDInterfaceMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) + +static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type); +static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len); +static void report_error (NCDInterfaceMonitor *o); +static int send_next_dump_request (NCDInterfaceMonitor *o); +static void netlink_fd_handler (NCDInterfaceMonitor *o, int events); +static void process_buffer (NCDInterfaceMonitor *o); +static void more_job_handler (NCDInterfaceMonitor *o); + +static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = seq; + req.g.rtgen_family = family; + + int res = write(fd, &req, sizeof(req)); + if (res < 0) { + BLog(BLOG_ERROR, "write failed"); + return 0; + } + if (res != sizeof(req)) { + BLog(BLOG_ERROR, "write short"); + return 0; + } + + return 1; +} + +static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len) +{ + for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) { + uint8_t *attr = RTA_DATA(rta); + int attr_len = RTA_PAYLOAD(rta); + + if (rta->rta_type == type) { + *out_attr = attr; + *out_attr_len = attr_len; + return 1; + } + } + + return 0; +} + +static void report_error (NCDInterfaceMonitor *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +static int send_next_dump_request (NCDInterfaceMonitor *o) +{ + ASSERT(o->dump_queue) + + if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK); + } + else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR); + } + else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR); + } + + ASSERT(0) + return 0; +} + +void netlink_fd_handler (NCDInterfaceMonitor *o, int events) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_bfd) + + // handler fd error + if (o->buf_left >= 0) { + BLog(BLOG_ERROR, "file descriptor error"); + goto fail; + } + + // read from netlink fd + int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf)); + if (len < 0) { + BLog(BLOG_ERROR, "read failed"); + goto fail; + } + + // stop receiving fd events + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0); + + // set buffer + o->buf_nh = &o->buf.nlh; + o->buf_left = len; + + // process buffer + process_buffer(o); + return; + +fail: + report_error(o); +} + +void process_buffer (NCDInterfaceMonitor *o) +{ + ASSERT(o->buf_left >= 0) + ASSERT(o->have_bfd) + + int done = 0; + + for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) { + if (o->buf_nh->nlmsg_type == NLMSG_DONE) { + done = 1; + break; + } + + struct nlmsghdr *buf = o->buf_nh; + void *pl = NLMSG_DATA(buf); + int pl_len = NLMSG_PAYLOAD(buf, 0); + + struct NCDInterfaceMonitor_event ev; + + switch (buf->nlmsg_type) { + case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean... + if (pl_len < sizeof(struct ifinfomsg)) { + BLog(BLOG_ERROR, "ifinfomsg too short"); + goto fail; + } + struct ifinfomsg *msg = pl; + + if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) { + ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN; + goto dispatch; + } + } break; + + case RTM_NEWADDR: + case RTM_DELADDR: { + if (pl_len < sizeof(struct ifaddrmsg)) { + BLog(BLOG_ERROR, "ifaddrmsg too short"); + goto fail; + } + struct ifaddrmsg *msg = pl; + + void *addr; + int addr_len; + if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) { + break; + } + + if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) { + if (addr_len != 4 || msg->ifa_prefixlen > 32) { + BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg"); + goto fail; + } + + ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED; + ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr; + ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen; + goto dispatch; + } + + if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) { + if (addr_len != 16 || msg->ifa_prefixlen > 128) { + BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg"); + goto fail; + } + + ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED; + memcpy(ev.u.ipv6_addr.addr.addr.bytes, ((struct in6_addr *)addr)->s6_addr, 16); + ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen; + ev.u.ipv6_addr.addr_flags = 0; + ev.u.ipv6_addr.scope = msg->ifa_scope; + if (!(msg->ifa_flags & IFA_F_PERMANENT)) { + ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC; + } + goto dispatch; + } + } break; + } + + continue; + + dispatch: + // move to next message + o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left); + + // schedule more job + BPending_Set(&o->more_job); + + // dispatch event + o->handler(o->user, ev); + return; + } + + if (done) { + if (o->dump_queue) { + // increment dump request sequence number + o->dump_seq++; + + // send next dump request + if (!send_next_dump_request(o)) { + goto fail; + } + } + else if (o->event_netlink_fd >= 0) { + // stop watching dump fd + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + o->have_bfd = 0; + + // close dump fd, make event fd current + close(o->netlink_fd); + o->netlink_fd = o->event_netlink_fd; + o->event_netlink_fd = -1; + + // start watching event fd + BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail; + } + o->have_bfd = 1; + } + } + + // set no buffer + o->buf_left = -1; + + // continue receiving fd events + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + return; + +fail: + report_error(o); +} + +void more_job_handler (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buf_left >= 0) + + // process buffer + process_buffer(o); + return; +} + +int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user, + NCDInterfaceMonitor_handler handler, + NCDInterfaceMonitor_handler_error handler_error) +{ + ASSERT(watch_events) + ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0) + ASSERT(handler) + ASSERT(handler_error) + BNetwork_Assert(); + + // init arguments + o->ifindex = ifindex; + o->watch_events = watch_events; + o->reactor = reactor; + o->user = user; + o->handler = handler; + o->handler_error = handler_error; + + // init dump netlink fd + if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + if (!badvpn_set_nonblocking(o->netlink_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init event netlink fd + if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + if (!badvpn_set_nonblocking(o->event_netlink_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // build bind address + struct sockaddr_nl sa; + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = 0; + if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK; + if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR; + if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR; + + // bind event netlink fd + if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail2; + } + + // set dump state + o->dump_queue = watch_events; + o->dump_seq = 0; + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o); + if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + o->have_bfd = 1; + + // set nothing in buffer + o->buf_left = -1; + + // init more job + BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o); + + // send first dump request + if (!send_next_dump_request(o)) { + goto fail3; + } + + // wait for reading fd + BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ); + + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + BPending_Free(&o->more_job); + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); +fail2: + close(o->event_netlink_fd); +fail1: + close(o->netlink_fd); +fail0: + return 0; +} + +void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free more job + BPending_Free(&o->more_job); + + // free BFileDescriptor + if (o->have_bfd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close event fd, in case we're still dumping + if (o->event_netlink_fd >= 0) { + close(o->event_netlink_fd); + } + + // close fd + close(o->netlink_fd); +} + +void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->have_bfd) + + if (o->buf_left >= 0) { + BPending_Unset(&o->more_job); + } else { + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0); + } +} + +void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->have_bfd) + + if (o->buf_left >= 0) { + BPending_Set(&o->more_job); + } else { + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + } +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.h b/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.h new file mode 100644 index 0000000..805b8de --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDInterfaceMonitor.h @@ -0,0 +1,160 @@ +/** + * @file NCDInterfaceMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDINTERFACEMONITOR_H +#define BADVPN_NCD_NCDINTERFACEMONITOR_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NCDIFMONITOR_WATCH_LINK (1 << 0) +#define NCDIFMONITOR_WATCH_IPV4_ADDR (1 << 1) +#define NCDIFMONITOR_WATCH_IPV6_ADDR (1 << 2) + +#define NCDIFMONITOR_EVENT_LINK_UP 1 +#define NCDIFMONITOR_EVENT_LINK_DOWN 2 +#define NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED 3 +#define NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED 4 +#define NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED 5 +#define NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED 6 + +#define NCDIFMONITOR_ADDR_FLAG_DYNAMIC (1 << 0) + +struct NCDInterfaceMonitor_event { + int event; + union { + struct { + struct ipv4_ifaddr addr; + } ipv4_addr; + struct { + struct ipv6_ifaddr addr; + int addr_flags; + uint8_t scope; + } ipv6_addr; + } u; +}; + +/** + * Handler called to report an interface event. + * Note that the event reporter does not keep any interface state, and as such may + * report redundant events. You should therefore handle events in an idempotent + * fashion. + * + * @param event.event event type. One of: + * - NCDIFMONITOR_EVENT_LINK_UP, NCDIFMONITOR_EVENT_LINK_DOWN, + * - NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED, + * - NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED. + * Only events that correspont to enabled watch flags are reported. + * @param event.ipv4_addr.addr the IPv4 address and prefix length + * @param event.ipv6_addr.addr the IPv6 address, prefix length and scope + * @param event.ipv6_addr.addr_flags IPv6 address flags. Valid flags: + * - NCDIFMONITOR_ADDR_FLAG_DYNAMIC - this address was assigned dynamically (NDP) + */ +typedef void (*NCDInterfaceMonitor_handler) (void *user, struct NCDInterfaceMonitor_event event); + +/** + * Handler called when an error occurs. + * The event reporter must be freed from within the job context of this + * handler, and no other operations must be performed. + */ +typedef void (*NCDInterfaceMonitor_handler_error) (void *user); + +/** + * Watches for network interface events using a Linux rtnetlink socket. + */ +typedef struct { + int ifindex; + int watch_events; + BReactor *reactor; + void *user; + NCDInterfaceMonitor_handler handler; + NCDInterfaceMonitor_handler_error handler_error; + int netlink_fd; + int event_netlink_fd; + int dump_queue; + uint32_t dump_seq; + BFileDescriptor bfd; + int have_bfd; + union { + uint8_t buf[4096]; + struct nlmsghdr nlh; + } buf; + struct nlmsghdr *buf_nh; + int buf_left; + BPending more_job; + DebugError d_err; + DebugObject d_obj; +} NCDInterfaceMonitor; + +/** + * Initializes the event reporter. + * The reporter is not paused initially. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param ifindex index of network interface to report events for + * @param watch_events mask specifying what kind of events to report. Valid flags are + * NCDIFMONITOR_WATCH_LINK, NCDIFMONITOR_WATCH_IPV4_ADDR, NCDIFMONITOR_WATCH_IPV6_ADDR. + * At least one flag must be provided. + * @param reactor reactor we live in + * @param user argument to handlers + * @param handler handler to report interface events to + * @param handler_error error handler + * @return 1 on success, 0 on failure + */ +int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user, + NCDInterfaceMonitor_handler handler, + NCDInterfaceMonitor_handler_error handler_error) WARN_UNUSED; + +/** + * Frees the event reporter. + */ +void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o); + +/** + * Pauses event reporting. + * This operation is idempotent. + */ +void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o); + +/** + * Resumes event reporting. + * This operation is idempotent. + */ +void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.c b/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.c new file mode 100644 index 0000000..edf6378 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.c @@ -0,0 +1,647 @@ +/** + * @file NCDRequestClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "NCDRequestClient.h" + +#include + +#define SEND_PAYLOAD_MTU 32768 +#define RECV_PAYLOAD_MTU 32768 + +#define SEND_MTU (SEND_PAYLOAD_MTU + sizeof(struct requestproto_header)) +#define RECV_MTU (RECV_PAYLOAD_MTU + sizeof(struct requestproto_header)) + +#define CSTATE_CONNECTING 1 +#define CSTATE_CONNECTED 2 + +#define RSTATE_SENDING_REQUEST 1 +#define RSTATE_READY 2 +#define RSTATE_SENDING_REQUEST_ABORT 3 +#define RSTATE_SENDING_ABORT 4 +#define RSTATE_WAITING_END 5 +#define RSTATE_DEAD_SENDING 6 + +static int uint32_comparator (void *unused, void *vv1, void *vv2); +static void report_error (NCDRequestClient *o); +static void request_report_finished (NCDRequestClientRequest *o, int is_error); +static void connector_handler (NCDRequestClient *o, int is_error); +static void connection_handler (NCDRequestClient *o, int event); +static void decoder_handler_error (NCDRequestClient *o); +static void recv_if_handler_send (NCDRequestClient *o, uint8_t *data, int data_len); +static struct NCDRequestClient_req * find_req (NCDRequestClient *o, uint32_t request_id); +static int get_free_request_id (NCDRequestClient *o, uint32_t *out); +static int build_requestproto_packet (uint32_t request_id, uint32_t type, NCDValRef payload_value, uint8_t **out_data, int *out_len); +static void build_nodata_packet (uint32_t request_id, uint32_t type, uint8_t *data, int *out_len); +static int req_is_aborted (struct NCDRequestClient_req *req); +static void req_abort (struct NCDRequestClient_req *req); +static void req_free (struct NCDRequestClient_req *req); +static void req_send_abort (struct NCDRequestClient_req *req); +static void req_qflow_send_iface_handler_done (struct NCDRequestClient_req *req); + +static int uint32_comparator (void *unused, void *vv1, void *vv2) +{ + uint32_t *v1 = vv1; + uint32_t *v2 = vv2; + return B_COMPARE(*v1, *v2); +} + +static void report_error (NCDRequestClient *o) +{ + ASSERT(!o->is_error) + + o->is_error = 1; + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +static void request_report_finished (NCDRequestClientRequest *o, int is_error) +{ + o->req = NULL; + + DEBUGERROR(&o->d_err, o->handler_finished(o->user, is_error)) +} + +static void connector_handler (NCDRequestClient *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTING) + + // check error + if (is_error) { + BLog(BLOG_ERROR, "failed to connect to socket"); + goto fail0; + } + + BPendingGroup *pg = BReactor_PendingGroup(o->reactor); + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->con); + BConnection_RecvAsync_Init(&o->con); + StreamPassInterface *con_send_if = BConnection_SendAsync_GetIf(&o->con); + StreamRecvInterface *con_recv_if = BConnection_RecvAsync_GetIf(&o->con); + + // init receive interface + PacketPassInterface_Init(&o->recv_if, RECV_MTU, (PacketPassInterface_handler_send)recv_if_handler_send, o, pg); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->recv_decoder, con_recv_if, &o->recv_if, pg, o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init send sender + PacketStreamSender_Init(&o->send_sender, con_send_if, PACKETPROTO_ENCLEN(SEND_MTU), pg); + + // init send queue + PacketPassFifoQueue_Init(&o->send_queue, PacketStreamSender_GetInput(&o->send_sender), pg); + + // set state connected + o->state = CSTATE_CONNECTED; + + // call connected handler + o->handler_connected(o->user); + return; + +fail1: + PacketPassInterface_Free(&o->recv_if); + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); +fail0: + report_error(o); +} + +static void connection_handler (NCDRequestClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + + BLog(BLOG_ERROR, "connection error"); + + report_error(o); +} + +static void decoder_handler_error (NCDRequestClient *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + + BLog(BLOG_ERROR, "decoder error"); + + report_error(o); +} + +static void recv_if_handler_send (NCDRequestClient *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_MTU) + + // accept packet + PacketPassInterface_Done(&o->recv_if); + + if (data_len < sizeof(struct requestproto_header)) { + BLog(BLOG_ERROR, "missing requestproto header"); + goto fail; + } + + struct requestproto_header header; + memcpy(&header, data, sizeof(header)); + uint32_t request_id = ltoh32(header.request_id); + uint32_t type = ltoh32(header.type); + + uint8_t *payload = data + sizeof(header); + int payload_len = data_len - sizeof(header); + + // find request + struct NCDRequestClient_req *req = find_req(o, request_id); + if (!req) { + BLog(BLOG_ERROR, "received packet with unknown request ID"); + goto fail; + } + + switch (type) { + case REQUESTPROTO_TYPE_SERVER_REPLY: { + switch (o->state) { + case RSTATE_READY: { + // init memory + NCDValMem mem; + NCDValMem_Init(&mem); + + // parse payload + NCDValRef payload_value; + if (!NCDValParser_Parse((char *)payload, payload_len, &mem, &payload_value)) { + BLog(BLOG_ERROR, "failed to parse reply payload"); + NCDValMem_Free(&mem); + goto fail; + } + + // call reply handler + req->creq->handler_reply(req->creq->user, mem, payload_value); + return; + } break; + + case RSTATE_SENDING_ABORT: + case RSTATE_WAITING_END: + return; + + default: + BLog(BLOG_ERROR, "received unexpected reply"); + goto fail; + } + } break; + + case REQUESTPROTO_TYPE_SERVER_FINISHED: + case REQUESTPROTO_TYPE_SERVER_ERROR: { + if (payload_len != 0) { + BLog(BLOG_ERROR, "finshed/aborted message has non-empty payload"); + goto fail; + } + + NCDRequestClientRequest *creq = req->creq; + req->creq = NULL; + + switch (req->state) { + case RSTATE_SENDING_ABORT: { + // set state dying send + req->state = RSTATE_DEAD_SENDING; + } break; + + case RSTATE_WAITING_END: + case RSTATE_READY: { + // free req + req_free(req); + } break; + + default: + BLog(BLOG_ERROR, "received unexpected finished/aborted"); + goto fail; + } + + // report finished + if (creq) { + request_report_finished(creq, type == REQUESTPROTO_TYPE_SERVER_ERROR); + } + return; + } break; + + default: + BLog(BLOG_ERROR, "received invalid message type"); + goto fail; + } + + ASSERT(0) + +fail: + report_error(o); +} + +static struct NCDRequestClient_req * find_req (NCDRequestClient *o, uint32_t request_id) +{ + BAVLNode *tn = BAVL_LookupExact(&o->reqs_tree, &request_id); + if (!tn) { + return NULL; + } + + struct NCDRequestClient_req *req = UPPER_OBJECT(tn, struct NCDRequestClient_req, reqs_tree_node); + ASSERT(req->request_id == request_id) + + return req; +} + +static int get_free_request_id (NCDRequestClient *o, uint32_t *out) +{ + uint32_t first = o->next_request_id; + + do { + if (!find_req(o, o->next_request_id)) { + *out = o->next_request_id; + return 1; + } + o->next_request_id++; + } while (o->next_request_id != first); + + return 0; +} + +static int build_requestproto_packet (uint32_t request_id, uint32_t type, NCDValRef payload_value, uint8_t **out_data, int *out_len) +{ + ExpString str; + if (!ExpString_Init(&str)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + if (!ExpString_AppendZeros(&str, sizeof(struct packetproto_header) + sizeof(struct requestproto_header))) { + BLog(BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + + if (!NCDVal_IsInvalid(payload_value) && !NCDValGenerator_AppendGenerate(payload_value, &str)) { + BLog(BLOG_ERROR, "NCDValGenerator_AppendGenerate failed"); + goto fail1; + } + + size_t len = ExpString_Length(&str); + if (len > INT_MAX || len > PACKETPROTO_ENCLEN(SEND_MTU) || len - sizeof(struct packetproto_header) > UINT16_MAX) { + BLog(BLOG_ERROR, "reply is too long"); + goto fail1; + } + + uint8_t *packet = (uint8_t *)ExpString_Get(&str); + + struct packetproto_header pp; + pp.len = htol16(len - sizeof(struct packetproto_header)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + rp.type = htol32(type); + + memcpy(packet, &pp, sizeof(pp)); + memcpy(packet + sizeof(pp), &rp, sizeof(rp)); + + *out_data = packet; + *out_len = len; + return 1; + +fail1: + ExpString_Free(&str); +fail0: + return 0; +} + +static void build_nodata_packet (uint32_t request_id, uint32_t type, uint8_t *data, int *out_len) +{ + struct packetproto_header pp; + pp.len = htol16(sizeof(struct requestproto_header)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + rp.type = htol32(type); + + memcpy(data, &pp, sizeof(pp)); + memcpy(data + sizeof(pp), &rp, sizeof(rp)); + + *out_len = sizeof(pp) + sizeof(rp); +} + +static int req_is_aborted (struct NCDRequestClient_req *req) +{ + switch (req->state) { + case RSTATE_SENDING_REQUEST: + case RSTATE_READY: + return 0; + default: + return 1; + } +} + +static void req_abort (struct NCDRequestClient_req *req) +{ + ASSERT(!req_is_aborted(req)) + ASSERT(!req->client->is_error) + + switch (req->state) { + case RSTATE_SENDING_REQUEST: { + req->state = RSTATE_SENDING_REQUEST_ABORT; + } break; + + case RSTATE_READY: { + req_send_abort(req); + } break; + + default: ASSERT(0); + } +} + +static void req_free (struct NCDRequestClient_req *req) +{ + NCDRequestClient *client = req->client; + PacketPassFifoQueueFlow_AssertFree(&req->send_qflow); + ASSERT(!req->creq) + + // free queue flow + PacketPassFifoQueueFlow_Free(&req->send_qflow); + + // free request data + free(req->request_data); + + // remove from reqs tree + BAVL_Remove(&client->reqs_tree, &req->reqs_tree_node); + + // free structure + free(req); +} + +static void req_send_abort (struct NCDRequestClient_req *req) +{ + // build packet + build_nodata_packet(req->request_id, REQUESTPROTO_TYPE_CLIENT_ABORT, req->request_data, &req->request_len); + + // start sending + PacketPassInterface_Sender_Send(req->send_qflow_iface, req->request_data, req->request_len); + + // set state sending abort + req->state = RSTATE_SENDING_ABORT; +} + +static void req_qflow_send_iface_handler_done (struct NCDRequestClient_req *req) +{ + switch (req->state) { + case RSTATE_SENDING_REQUEST: { + // set state ready + req->state = RSTATE_READY; + + // call sent handler + req->creq->handler_sent(req->creq->user); + return; + } break; + + case RSTATE_SENDING_REQUEST_ABORT: { + // send abort + req_send_abort(req); + } break; + + case RSTATE_SENDING_ABORT: { + // set state waiting end + req->state = RSTATE_WAITING_END; + } break; + + case RSTATE_DEAD_SENDING: { + // free req + req_free(req); + } break; + + default: ASSERT(0); + } +} + +int NCDRequestClient_Init (NCDRequestClient *o, struct BConnection_addr addr, BReactor *reactor, void *user, + NCDRequestClient_handler_error handler_error, + NCDRequestClient_handler_connected handler_connected) +{ + ASSERT(handler_error) + ASSERT(handler_connected) + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler_error = handler_error; + o->handler_connected = handler_connected; + + // init connector + if (!BConnector_InitGeneric(&o->connector, addr, reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_InitGeneric failed"); + goto fail0; + } + + // init reqs tree + BAVL_Init(&o->reqs_tree, OFFSET_DIFF(struct NCDRequestClient_req, request_id, reqs_tree_node), uint32_comparator, NULL); + + // set next request ID + o->next_request_id = 0; + + // set state connecting + o->state = CSTATE_CONNECTING; + + // set is not error + o->is_error = 0; + + DebugCounter_Init(&o->d_reqests_ctr); + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void NCDRequestClient_Free (NCDRequestClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + DebugCounter_Free(&o->d_reqests_ctr); + + if (o->state == CSTATE_CONNECTED) { + // allow freeing queue flow + PacketPassFifoQueue_PrepareFree(&o->send_queue); + + // free remaining reqs + BAVLNode *tn; + while (tn = BAVL_GetFirst(&o->reqs_tree)) { + struct NCDRequestClient_req *req = UPPER_OBJECT(tn, struct NCDRequestClient_req, reqs_tree_node); + ASSERT(!req->creq) + req_free(req); + } + + // free connection stuff + PacketPassFifoQueue_Free(&o->send_queue); + PacketStreamSender_Free(&o->send_sender); + PacketProtoDecoder_Free(&o->recv_decoder); + PacketPassInterface_Free(&o->recv_if); + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); + } + + // free connector + BConnector_Free(&o->connector); +} + +int NCDRequestClientRequest_Init (NCDRequestClientRequest *o, NCDRequestClient *client, NCDValRef payload_value, void *user, + NCDRequestClientRequest_handler_sent handler_sent, + NCDRequestClientRequest_handler_reply handler_reply, + NCDRequestClientRequest_handler_finished handler_finished) +{ + ASSERT(client->state == CSTATE_CONNECTED) + DebugError_AssertNoError(&client->d_err); + ASSERT(!NCDVal_IsInvalid(payload_value)) + ASSERT(handler_sent) + ASSERT(handler_reply) + ASSERT(handler_finished) + + // init arguments + o->client = client; + o->user = user; + o->handler_sent = handler_sent; + o->handler_reply = handler_reply; + o->handler_finished = handler_finished; + + // allocate req structure + struct NCDRequestClient_req *req = malloc(sizeof(*req)); + if (!req) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // allocate request ID + if (!get_free_request_id(client, &req->request_id)) { + BLog(BLOG_ERROR, "failed to allocate request ID"); + goto fail1; + } + + // insert to reqs tree + int res = BAVL_Insert(&client->reqs_tree, &req->reqs_tree_node, NULL); + ASSERT_EXECUTE(res) + + // set pointers + o->req = req; + req->creq = o; + req->client = client; + + // build request + if (!build_requestproto_packet(req->request_id, REQUESTPROTO_TYPE_CLIENT_REQUEST, payload_value, &req->request_data, &req->request_len)) { + BLog(BLOG_ERROR, "failed to build request"); + goto fail2; + } + + // init queue flow + PacketPassFifoQueueFlow_Init(&req->send_qflow, &client->send_queue); + + // init send interface + req->send_qflow_iface = PacketPassFifoQueueFlow_GetInput(&req->send_qflow); + PacketPassInterface_Sender_Init(req->send_qflow_iface, (PacketPassInterface_handler_done)req_qflow_send_iface_handler_done, req); + + // start sending request + PacketPassInterface_Sender_Send(req->send_qflow_iface, req->request_data, req->request_len); + + // set state sending request + req->state = RSTATE_SENDING_REQUEST; + + DebugCounter_Increment(&client->d_reqests_ctr); + DebugError_Init(&o->d_err, BReactor_PendingGroup(client->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + BAVL_Remove(&client->reqs_tree, &req->reqs_tree_node); +fail1: + free(req); +fail0: + return 0; +} + +void NCDRequestClientRequest_Free (NCDRequestClientRequest *o) +{ + struct NCDRequestClient_req *req = o->req; + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + DebugCounter_Decrement(&o->client->d_reqests_ctr); + + if (req) { + ASSERT(req->creq == o) + + // remove reference to us + req->creq = NULL; + + // abort req if not already + if (!req->client->is_error && !req_is_aborted(req)) { + req_abort(req); + } + } +} + +void NCDRequestClientRequest_Abort (NCDRequestClientRequest *o) +{ + struct NCDRequestClient_req *req = o->req; + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + DebugError_AssertNoError(&o->client->d_err); + ASSERT(req) + ASSERT(req->creq == o) + ASSERT(!req_is_aborted(req)) + + // abort req + req_abort(req); +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.h b/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.h new file mode 100644 index 0000000..7e0a6d5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDRequestClient.h @@ -0,0 +1,111 @@ +/** + * @file NCDRequestClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDREQUESTCLIENT_H +#define BADVPN_NCDREQUESTCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct NCDRequestClient_req; + +typedef void (*NCDRequestClient_handler_error) (void *user); +typedef void (*NCDRequestClient_handler_connected) (void *user); +typedef void (*NCDRequestClientRequest_handler_sent) (void *user); +typedef void (*NCDRequestClientRequest_handler_reply) (void *user, NCDValMem reply_mem, NCDValRef reply_value); +typedef void (*NCDRequestClientRequest_handler_finished) (void *user, int is_error); + +typedef struct { + BReactor *reactor; + void *user; + NCDRequestClient_handler_error handler_error; + NCDRequestClient_handler_connected handler_connected; + BConnector connector; + BConnection con; + PacketPassFifoQueue send_queue; + PacketStreamSender send_sender; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + BAVL reqs_tree; + uint32_t next_request_id; + int state; + int is_error; + DebugCounter d_reqests_ctr; + DebugError d_err; + DebugObject d_obj; +} NCDRequestClient; + +typedef struct { + NCDRequestClient *client; + void *user; + NCDRequestClientRequest_handler_sent handler_sent; + NCDRequestClientRequest_handler_reply handler_reply; + NCDRequestClientRequest_handler_finished handler_finished; + struct NCDRequestClient_req *req; + DebugError d_err; + DebugObject d_obj; +} NCDRequestClientRequest; + +struct NCDRequestClient_req { + NCDRequestClientRequest *creq; + NCDRequestClient *client; + BAVLNode reqs_tree_node; + uint32_t request_id; + uint8_t *request_data; + int request_len; + PacketPassInterface *send_qflow_iface; + PacketPassFifoQueueFlow send_qflow; + int state; +}; + +int NCDRequestClient_Init (NCDRequestClient *o, struct BConnection_addr addr, BReactor *reactor, void *user, + NCDRequestClient_handler_error handler_error, + NCDRequestClient_handler_connected handler_connected) WARN_UNUSED; +void NCDRequestClient_Free (NCDRequestClient *o); + +int NCDRequestClientRequest_Init (NCDRequestClientRequest *o, NCDRequestClient *client, NCDValRef payload_value, void *user, + NCDRequestClientRequest_handler_sent handler_sent, + NCDRequestClientRequest_handler_reply handler_reply, + NCDRequestClientRequest_handler_finished handler_finished) WARN_UNUSED; +void NCDRequestClientRequest_Free (NCDRequestClientRequest *o); +void NCDRequestClientRequest_Abort (NCDRequestClientRequest *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.c b/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.c new file mode 100644 index 0000000..cec2a3d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.c @@ -0,0 +1,117 @@ +/** + * @file NCDRfkillMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "NCDRfkillMonitor.h" + +#include + +#define RFKILL_DEVICE_NODE "/dev/rfkill" + +static void rfkill_fd_handler (NCDRfkillMonitor *o, int events); + +void rfkill_fd_handler (NCDRfkillMonitor *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // read from netlink fd + struct rfkill_event event; + int len = read(o->rfkill_fd, &event, sizeof(event)); + if (len < 0) { + BLog(BLOG_ERROR, "read failed"); + return; + } + if (len != sizeof(event)) { + BLog(BLOG_ERROR, "read returned wrong length"); + return; + } + + // call handler + o->handler(o->user, event); + return; +} + +int NCDRfkillMonitor_Init (NCDRfkillMonitor *o, BReactor *reactor, NCDRfkillMonitor_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // open rfkill + if ((o->rfkill_fd = open(RFKILL_DEVICE_NODE, O_RDONLY)) < 0) { + BLog(BLOG_ERROR, "open failed"); + goto fail0; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->rfkill_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->rfkill_fd, (BFileDescriptor_handler)rfkill_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->rfkill_fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void NCDRfkillMonitor_Free (NCDRfkillMonitor *o) +{ + DebugObject_Free(&o->d_obj); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // close rfkill + if (close(o->rfkill_fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} diff --git a/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.h b/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.h new file mode 100644 index 0000000..00aa265 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/NCDRfkillMonitor.h @@ -0,0 +1,53 @@ +/** + * @file NCDRfkillMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDRFKILLMONITOR_H +#define BADVPN_NCD_NCDRFKILLMONITOR_H + +#include + +#include +#include +#include + +typedef void (*NCDRfkillMonitor_handler) (void *user, struct rfkill_event event); + +typedef struct { + BReactor *reactor; + NCDRfkillMonitor_handler handler; + void *user; + int rfkill_fd; + BFileDescriptor bfd; + DebugObject d_obj; +} NCDRfkillMonitor; + +int NCDRfkillMonitor_Init (NCDRfkillMonitor *o, BReactor *reactor, NCDRfkillMonitor_handler handler, void *user) WARN_UNUSED; +void NCDRfkillMonitor_Free (NCDRfkillMonitor *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/address_utils.h b/app/src/main/jni/badvpn/ncd/extra/address_utils.h new file mode 100644 index 0000000..24ff79a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/address_utils.h @@ -0,0 +1,280 @@ +/** + * @file address_utils.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_ADDRESS_UTILS_H +#define NCD_ADDRESS_UTILS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ncd_read_baddr (NCDValRef val, BAddr *out) WARN_UNUSED; +static NCDValRef ncd_make_baddr (BAddr addr, NCDValMem *mem); +static int ncd_read_bconnection_addr (NCDValRef val, struct BConnection_addr *out_addr) WARN_UNUSED; + +static int ncd_read_baddr (NCDValRef val, BAddr *out) +{ + ASSERT(!NCDVal_IsInvalid(val)) + ASSERT(NCDVal_HasOnlyContinuousStrings(val)) + ASSERT(out) + + if (!NCDVal_IsList(val)) { + goto fail; + } + + NCDValRef type_val; + if (!NCDVal_ListReadHead(val, 1, &type_val)) { + goto fail; + } + if (!NCDVal_IsString(type_val)) { + goto fail; + } + + BAddr addr; + + if (NCDVal_StringEquals(type_val, "none")) { + if (!NCDVal_ListRead(val, 1, &type_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_NONE; + } + else if (NCDVal_StringEquals(type_val, "ipv4")) { + NCDValRef ipaddr_val; + NCDValRef port_val; + if (!NCDVal_ListRead(val, 3, &type_val, &ipaddr_val, &port_val)) { + goto fail; + } + if (!NCDVal_IsString(ipaddr_val) || !NCDVal_IsString(port_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_IPV4; + + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(ipaddr_val), NCDVal_StringLength(ipaddr_val), &addr.ipv4.ip)) { + goto fail; + } + + uintmax_t port; + if (!ncd_read_uintmax(port_val, &port) || port > UINT16_MAX) { + goto fail; + } + addr.ipv4.port = hton16(port); + } + else if (NCDVal_StringEquals(type_val, "ipv6")) { + NCDValRef ipaddr_val; + NCDValRef port_val; + if (!NCDVal_ListRead(val, 3, &type_val, &ipaddr_val, &port_val)) { + goto fail; + } + if (!NCDVal_IsString(ipaddr_val) || !NCDVal_IsString(port_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_IPV6; + + struct ipv6_addr i6addr; + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(ipaddr_val), NCDVal_StringLength(ipaddr_val), &i6addr)) { + goto fail; + } + memcpy(addr.ipv6.ip, i6addr.bytes, 16); + + uintmax_t port; + if (!ncd_read_uintmax(port_val, &port) || port > UINT16_MAX) { + goto fail; + } + addr.ipv6.port = hton16(port); + } + else { + goto fail; + } + + *out = addr; + return 1; + +fail: + return 0; +} + +static NCDValRef ncd_make_baddr (BAddr addr, NCDValMem *mem) +{ + BAddr_Assert(&addr); + ASSERT(mem) + + NCDValRef val; + + switch (addr.type) { + default: + case BADDR_TYPE_NONE: { + val = NCDVal_NewList(mem, 1); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + const char *str = (addr.type == BADDR_TYPE_NONE ? "none" : "unknown"); + NCDValRef type_val = NCDVal_NewString(mem, str); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + } break; + + case BADDR_TYPE_IPV4: { + val = NCDVal_NewList(mem, 3); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + NCDValRef type_val = NCDVal_NewString(mem, "ipv4"); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + char ipaddr_buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr.ipv4.ip, ipaddr_buf); + NCDValRef ipaddr_val = NCDVal_NewString(mem, ipaddr_buf); + if (NCDVal_IsInvalid(ipaddr_val)) { + goto fail; + } + + NCDValRef port_val = ncd_make_uintmax(mem, ntoh16(addr.ipv4.port)); + if (NCDVal_IsInvalid(port_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, ipaddr_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, port_val)) { + goto fail; + } + } break; + + case BADDR_TYPE_IPV6: { + val = NCDVal_NewList(mem, 3); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + NCDValRef type_val = NCDVal_NewString(mem, "ipv6"); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + char ipaddr_buf[IPADDR6_PRINT_MAX]; + struct ipv6_addr i6addr; + memcpy(i6addr.bytes, addr.ipv6.ip, 16); + ipaddr6_print_addr(i6addr, ipaddr_buf); + NCDValRef ipaddr_val = NCDVal_NewString(mem, ipaddr_buf); + if (NCDVal_IsInvalid(ipaddr_val)) { + goto fail; + } + + NCDValRef port_val = ncd_make_uintmax(mem, ntoh16(addr.ipv6.port)); + if (NCDVal_IsInvalid(port_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, ipaddr_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, port_val)) { + goto fail; + } + } break; + } + + return val; + +fail: + return NCDVal_NewInvalid(); +} + +static int ncd_read_bconnection_addr (NCDValRef val, struct BConnection_addr *out_addr) +{ + ASSERT(!NCDVal_IsInvalid(val)) + ASSERT(NCDVal_HasOnlyContinuousStrings(val)) + + if (!NCDVal_IsList(val)) { + goto fail; + } + + NCDValRef protocol_arg; + NCDValRef data_arg; + if (!NCDVal_ListRead(val, 2, &protocol_arg, &data_arg)) { + goto fail; + } + + if (!NCDVal_IsString(protocol_arg)) { + goto fail; + } + + if (NCDVal_StringEquals(protocol_arg, "unix")) { + if (!NCDVal_IsStringNoNulls(data_arg)) { + goto fail; + } + + *out_addr = BConnection_addr_unix(NCDVal_StringData(data_arg), NCDVal_StringLength(data_arg)); + } + else if (NCDVal_StringEquals(protocol_arg, "tcp")) { + BAddr baddr; + if (!ncd_read_baddr(data_arg, &baddr)) { + goto fail; + } + + *out_addr = BConnection_addr_baddr(baddr); + } + else { + goto fail; + } + + return 1; + +fail: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/build_cmdline.c b/app/src/main/jni/badvpn/ncd/extra/build_cmdline.c new file mode 100644 index 0000000..ea96b0f --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/build_cmdline.c @@ -0,0 +1,111 @@ +/** + * @file build_cmdline.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "build_cmdline.h" + +int ncd_build_cmdline (NCDModuleInst *i, int log_channel, NCDValRef cmd_arg, char **out_exec, CmdLine *out_cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + ASSERT(out_exec) + ASSERT(out_cl) + + if (!NCDVal_IsList(cmd_arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail0; + } + char *exec = ncd_strdup(exec_arg); + if (!exec) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + CmdLine cl; + if (!CmdLine_Init(&cl)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(&cl, exec)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(&cl, cstr, 0, cstr.length)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(&cl)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + *out_exec = exec; + *out_cl = cl; + return 1; + +fail2: + CmdLine_Free(&cl); +fail1: + free(exec); +fail0: + return 0; +} diff --git a/app/src/main/jni/badvpn/ncd/extra/build_cmdline.h b/app/src/main/jni/badvpn/ncd/extra/build_cmdline.h new file mode 100644 index 0000000..27abf18 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/build_cmdline.h @@ -0,0 +1,38 @@ +/** + * @file build_cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BUILD_CMDLINE_H +#define NCD_BUILD_CMDLINE_H + +#include +#include + +int ncd_build_cmdline (NCDModuleInst *i, int log_channel, NCDValRef cmd_arg, char **exec, CmdLine *cl); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/extra/make_fast_names.h b/app/src/main/jni/badvpn/ncd/extra/make_fast_names.h new file mode 100644 index 0000000..3b7d72a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/make_fast_names.h @@ -0,0 +1,154 @@ +/** + * @file make_fast_names.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +// Input parameters: +// #define NAMES_PARAM_NAME +// #define NAMES_PARAM_TYPE struct instance +// #define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +// #define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +// #define NAMES_PARAM_MEMBER_NUM_NAMES num_names +// #define NAMES_PARAM_NUM_STATIC_NAMES 10 + +#define MakeFastNames_count_names MERGE(NAMES_PARAM_NAME, _count_names) +#define MakeFastNames_add_name MERGE(NAMES_PARAM_NAME, _add_name) +#define MakeFastNames_InitNames MERGE(NAMES_PARAM_NAME, _InitNames) +#define MakeFastNames_FreeNames MERGE(NAMES_PARAM_NAME, _FreeNames) +#define MakeFastNames_GetNames MERGE(NAMES_PARAM_NAME, _GetNames) + +static size_t MakeFastNames_count_names (const char *str, size_t str_len) +{ + size_t count = 1; + + while (str_len > 0) { + if (*str == '.') { + count++; + } + str++; + str_len--; + } + + return count; +} + +static int MakeFastNames_add_name (NAMES_PARAM_TYPE *o, NCDStringIndex *string_index, const char *str, size_t str_len, const char *remain, size_t remain_len) +{ + ASSERT(str) + ASSERT(!!o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES == (o->NAMES_PARAM_MEMBER_NUM_NAMES > NAMES_PARAM_NUM_STATIC_NAMES)) + + NCD_string_id_t id = NCDStringIndex_GetBin(string_index, str, str_len); + if (id < 0) { + return 0; + } + + if (o->NAMES_PARAM_MEMBER_NUM_NAMES < NAMES_PARAM_NUM_STATIC_NAMES) { + o->NAMES_PARAM_MEMBER_STATIC_NAMES[o->NAMES_PARAM_MEMBER_NUM_NAMES++] = id; + return 1; + } + + if (o->NAMES_PARAM_MEMBER_NUM_NAMES == NAMES_PARAM_NUM_STATIC_NAMES) { + size_t num_more = (!remain ? 0 : MakeFastNames_count_names(remain, remain_len)); + size_t num_all = o->NAMES_PARAM_MEMBER_NUM_NAMES + 1 + num_more; + + if (!(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES = BAllocArray(num_all, sizeof(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[0])))) { + return 0; + } + + memcpy(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES, o->NAMES_PARAM_MEMBER_STATIC_NAMES, NAMES_PARAM_NUM_STATIC_NAMES * sizeof(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[0])); + } + + o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[o->NAMES_PARAM_MEMBER_NUM_NAMES++] = id; + + return 1; +} + +static int MakeFastNames_InitNames (NAMES_PARAM_TYPE *o, NCDStringIndex *string_index, const char *str, size_t str_len) +{ + ASSERT(str) + + o->NAMES_PARAM_MEMBER_NUM_NAMES = 0; + o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES = NULL; + + size_t i = 0; + while (i < str_len) { + if (str[i] == '.') { + if (!MakeFastNames_add_name(o, string_index, str, i, str + (i + 1), str_len - (i + 1))) { + goto fail; + } + str += i + 1; + str_len -= i + 1; + i = 0; + continue; + } + i++; + } + + if (!MakeFastNames_add_name(o, string_index, str, i, NULL, 0)) { + goto fail; + } + + return 1; + +fail: + BFree(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES); + return 0; +} + +static void MakeFastNames_FreeNames (NAMES_PARAM_TYPE *o) +{ + if (o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES) { + BFree(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES); + } +} + +static NCD_string_id_t * MakeFastNames_GetNames (NAMES_PARAM_TYPE *o) +{ + ASSERT(o->NAMES_PARAM_MEMBER_NUM_NAMES > 0) + + return (o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES ? o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES : o->NAMES_PARAM_MEMBER_STATIC_NAMES); +} + +#undef MakeFastNames_count_names +#undef MakeFastNames_add_name +#undef MakeFastNames_InitNames +#undef MakeFastNames_FreeNames +#undef MakeFastNames_GetNames + +#undef NAMES_PARAM_NAME +#undef NAMES_PARAM_TYPE +#undef NAMES_PARAM_MEMBER_DYNAMIC_NAMES +#undef NAMES_PARAM_MEMBER_STATIC_NAMES +#undef NAMES_PARAM_MEMBER_NUM_NAMES +#undef NAMES_PARAM_NUM_STATIC_NAMES diff --git a/app/src/main/jni/badvpn/ncd/extra/value_utils.h b/app/src/main/jni/badvpn/ncd/extra/value_utils.h new file mode 100644 index 0000000..2d01148 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/extra/value_utils.h @@ -0,0 +1,174 @@ +/** + * @file value_utils.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_VALUE_UTILS_H +#define NCD_VALUE_UTILS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ncd_is_none (NCDValRef val); +static NCDValRef ncd_make_boolean (NCDValMem *mem, int value, NCDStringIndex *string_index); +static int ncd_read_boolean (NCDValRef val); +static int ncd_read_uintmax (NCDValRef string, uintmax_t *out) WARN_UNUSED; +static int ncd_read_time (NCDValRef string, btime_t *out) WARN_UNUSED; +static NCD_string_id_t ncd_get_string_id (NCDValRef string, NCDStringIndex *string_index); +static NCDValRef ncd_make_uintmax (NCDValMem *mem, uintmax_t value); +static char * ncd_strdup (NCDValRef stringnonulls); + +static int ncd_is_none (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string) == NCD_STRING_NONE; + } else { + return NCDVal_StringEquals(string, ""); + } +} + +static NCDValRef ncd_make_boolean (NCDValMem *mem, int value, NCDStringIndex *string_index) +{ + ASSERT(mem) + ASSERT(string_index) + + NCD_string_id_t str_id = (value ? NCD_STRING_TRUE : NCD_STRING_FALSE); + return NCDVal_NewIdString(mem, str_id, string_index); +} + +static int ncd_read_boolean (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string) == NCD_STRING_TRUE; + } else { + return NCDVal_StringEquals(string, "true"); + } +} + +static int ncd_read_uintmax (NCDValRef string, uintmax_t *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + size_t length = NCDVal_StringLength(string); + + if (NCDVal_IsContinuousString(string)) { + return parse_unsigned_integer_bin(NCDVal_StringData(string), length, out); + } + + b_cstring cstr = NCDVal_StringCstring(string); + + return parse_unsigned_integer_cstr(cstr, 0, cstr.length, out); +} + +static int ncd_read_time (NCDValRef string, btime_t *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + uintmax_t n; + if (!ncd_read_uintmax(string, &n)) { + return 0; + } + + if (n > INT64_MAX) { + return 0; + } + + *out = n; + return 1; +} + +static NCD_string_id_t ncd_get_string_id (NCDValRef string, NCDStringIndex *string_index) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(string_index) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string); + } else if (NCDVal_IsContinuousString(string)) { + return NCDStringIndex_GetBin(string_index, NCDVal_StringData(string), NCDVal_StringLength(string)); + } + + b_cstring cstr = NCDVal_StringCstring(string); + + char *temp = b_cstring_strdup(cstr, 0, cstr.length); + if (!temp) { + return -1; + } + + NCD_string_id_t res = NCDStringIndex_GetBin(string_index, temp, cstr.length); + BFree(temp); + + return res; +} + +static NCDValRef ncd_make_uintmax (NCDValMem *mem, uintmax_t value) +{ + ASSERT(mem) + + int size = compute_decimal_repr_size(value); + + NCDValRef val = NCDVal_NewStringUninitialized(mem, size); + + if (!NCDVal_IsInvalid(val)) { + char *data = (char *)NCDVal_StringData(val); + generate_decimal_repr(value, data, size); + } + + return val; +} + +static char * ncd_strdup (NCDValRef stringnonulls) +{ + ASSERT(NCDVal_IsStringNoNulls(stringnonulls)) + + size_t length = NCDVal_StringLength(stringnonulls); + + if (NCDVal_IsContinuousString(stringnonulls)) { + return b_strdup_bin(NCDVal_StringData(stringnonulls), length); + } + + b_cstring cstr = NCDVal_StringCstring(stringnonulls); + + return b_cstring_strdup(cstr, 0, cstr.length); +} + +#endif diff --git a/app/src/main/jni/badvpn/ncd/include_linux_input.c b/app/src/main/jni/badvpn/ncd/include_linux_input.c new file mode 100644 index 0000000..8eb6c02 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/include_linux_input.c @@ -0,0 +1 @@ +#include diff --git a/app/src/main/jni/badvpn/ncd/make_name_indices.h b/app/src/main/jni/badvpn/ncd/make_name_indices.h new file mode 100644 index 0000000..b5c7c8e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/make_name_indices.h @@ -0,0 +1,104 @@ +/** + * @file make_name_indices.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MAKE_NAME_INDICES_H +#define BADVPN_MAKE_NAME_INDICES_H + +#include +#include + +#include +#include +#include +#include + +static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names) WARN_UNUSED; + +static size_t split_string_inplace2__indices (char *str, char del) +{ + ASSERT(str) + + size_t num_extra_parts = 0; + + while (*str) { + if (*str == del) { + *str = '\0'; + num_extra_parts++; + } + str++; + } + + return num_extra_parts; +} + +static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names) +{ + ASSERT(string_index) + ASSERT(name) + ASSERT(out_varnames) + ASSERT(out_num_names) + + char *data = b_strdup(name); + if (!data) { + goto fail0; + } + + size_t num_names = split_string_inplace2__indices(data, '.') + 1; + + NCD_string_id_t *varnames = BAllocArray(num_names, sizeof(varnames[0])); + if (!varnames) { + goto fail1; + } + + char *cur = data; + for (size_t i = 0; i < num_names; i++) { + NCD_string_id_t id = NCDStringIndex_Get(string_index, cur); + if (id < 0) { + goto fail2; + } + + varnames[i] = id; + cur += strlen(cur) + 1; + } + + free(data); + + *out_varnames = varnames; + *out_num_names = num_names; + return 1; + +fail2: + BFree(varnames); +fail1: + free(data); +fail0: + return 0; +} + +#endif diff --git a/app/src/main/jni/badvpn/ncd/modules/alias.c b/app/src/main/jni/badvpn/ncd/modules/alias.c new file mode 100644 index 0000000..f6bb7ec --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/alias.c @@ -0,0 +1,148 @@ +/** + * @file alias.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * alias(string target) + * + * Variables and objects: + * - empty name - resolves target + * - nonempty name N - resolves target.N + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define NUM_STATIC_NAMES 4 + +struct instance { + NCDModuleInst *i; + NCD_string_id_t *dynamic_names; + size_t num_names; + NCD_string_id_t static_names[NUM_STATIC_NAMES]; +}; + +#define NAMES_PARAM_NAME AliasNames +#define NAMES_PARAM_TYPE struct instance +#define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +#define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +#define NAMES_PARAM_MEMBER_NUM_NAMES num_names +#define NAMES_PARAM_NUM_STATIC_NAMES NUM_STATIC_NAMES +#include + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef target_arg; + if (!NCDVal_ListRead(params->args, 1, &target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse name string + if (!AliasNames_InitNames(o, i->params->iparams->string_index, NCDVal_StringData(target_arg), NCDVal_StringLength(target_arg))) { + ModuleLog(i, BLOG_ERROR, "make_names failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + AliasNames_FreeNames(o); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = vo; + ASSERT(o->num_names > 0) + + NCD_string_id_t *names = AliasNames_GetNames(o); + + NCDObject object; + if (!NCDModuleInst_Backend_GetObj(o->i, names[0], &object)) { + return 0; + } + + NCDObject obj2; + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, o->num_names - 1, &obj2)) { + return 0; + } + + if (name == NCD_STRING_EMPTY) { + *out_object = obj2; + return 1; + } + + return NCDObject_GetObj(&obj2, name, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "alias", + .func_new2 = func_new, + .func_die = func_die, + .func_getobj = func_getobj, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_alias = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/arithmetic.c b/app/src/main/jni/badvpn/ncd/modules/arithmetic.c new file mode 100644 index 0000000..288a637 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/arithmetic.c @@ -0,0 +1,404 @@ +/** + * @file arithmetic.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Arithmetic functions for unsigned integers. + * + * Synopsis: + * num_lesser(string n1, string n2) + * num_greater(string n1, string n2) + * num_lesser_equal(string n1, string n2) + * num_greater_equal(string n1, string n2) + * num_equal(string n1, string n2) + * num_different(string n1, string n2) + * + * Variables: + * (empty) - "true" or "false", reflecting the value of the relation in question + * + * Description: + * These statements perform arithmetic comparisons. The operands passed must be + * non-negative decimal integers representable in a uintmax_t. Otherwise, an error + * is triggered. + * + * Synopsis: + * num_add(string n1, string n2) + * num_subtract(string n1, string n2) + * num_multiply(string n1, string n2) + * num_divide(string n1, string n2) + * num_modulo(string n1, string n2) + * + * Description: + * These statements perform arithmetic operations. The operands passed must be + * non-negative decimal integers representable in a uintmax_t, and the result must + * also be representable and non-negative. For divide and modulo, n2 must be non-zero. + * If any of these restrictions is violated, an error is triggered. + * + * Variables: + * (empty) - the result of the operation as a string representing a decimal number + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct boolean_instance { + NCDModuleInst *i; + int value; +}; + +typedef int (*boolean_compute_func) (uintmax_t n1, uintmax_t n2); + +struct number_instance { + NCDModuleInst *i; + uintmax_t value; +}; + +typedef int (*number_compute_func) (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out); + +static int compute_lesser (uintmax_t n1, uintmax_t n2) +{ + return n1 < n2; +} + +static int compute_greater (uintmax_t n1, uintmax_t n2) +{ + return n1 > n2; +} + +static int compute_lesser_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 <= n2; +} + +static int compute_greater_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 >= n2; +} + +static int compute_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 == n2; +} + +static int compute_different (uintmax_t n1, uintmax_t n2) +{ + return n1 != n2; +} + +static int compute_add (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 > UINTMAX_MAX - n2) { + ModuleLog(i, BLOG_ERROR, "addition overflow"); + return 0; + } + *out = n1 + n2; + return 1; +} + +static int compute_subtract (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 < n2) { + ModuleLog(i, BLOG_ERROR, "subtraction underflow"); + return 0; + } + *out = n1 - n2; + return 1; +} + +static int compute_multiply (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 > UINTMAX_MAX / n2) { + ModuleLog(i, BLOG_ERROR, "multiplication overflow"); + return 0; + } + *out = n1 * n2; + return 1; +} + +static int compute_divide (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n2 == 0) { + ModuleLog(i, BLOG_ERROR, "division quotient is zero"); + return 0; + } + *out = n1 / n2; + return 1; +} + +static int compute_modulo (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n2 == 0) { + ModuleLog(i, BLOG_ERROR, "modulo modulus is zero"); + return 0; + } + *out = n1 % n2; + return 1; +} + +static void new_boolean_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, boolean_compute_func cfunc) +{ + struct boolean_instance *o = vo; + o->i = i; + + NCDValRef n1_arg; + NCDValRef n2_arg; + if (!NCDVal_ListRead(params->args, 2, &n1_arg, &n2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(n1_arg) || !NCDVal_IsString(n2_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t n1; + if (!ncd_read_uintmax(n1_arg, &n1)) { + ModuleLog(o->i, BLOG_ERROR, "wrong first argument"); + goto fail0; + } + + uintmax_t n2; + if (!ncd_read_uintmax(n2_arg, &n2)) { + ModuleLog(o->i, BLOG_ERROR, "wrong second argument"); + goto fail0; + } + + o->value = cfunc(n1, n2); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int boolean_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct boolean_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void new_number_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, number_compute_func cfunc) +{ + struct number_instance *o = vo; + o->i = i; + + NCDValRef n1_arg; + NCDValRef n2_arg; + if (!NCDVal_ListRead(params->args, 2, &n1_arg, &n2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(n1_arg) || !NCDVal_IsString(n2_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t n1; + if (!ncd_read_uintmax(n1_arg, &n1)) { + ModuleLog(o->i, BLOG_ERROR, "wrong first argument"); + goto fail0; + } + + uintmax_t n2; + if (!ncd_read_uintmax(n2_arg, &n2)) { + ModuleLog(o->i, BLOG_ERROR, "wrong second argument"); + goto fail0; + } + + if (!cfunc(i, n1, n2, &o->value)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int number_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct number_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_uintmax(mem, o->value); + return 1; + } + + return 0; +} + +static void func_new_lesser (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_lesser); +} + +static void func_new_greater (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_greater); +} + +static void func_new_lesser_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_lesser_equal); +} + +static void func_new_greater_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_greater_equal); +} + +static void func_new_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_equal); +} + +static void func_new_different (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_different); +} + +static void func_new_add (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_add); +} + +static void func_new_subtract (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_subtract); +} + +static void func_new_multiply (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_multiply); +} + +static void func_new_divide (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_divide); +} + +static void func_new_modulo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_modulo); +} + +static struct NCDModule modules[] = { + { + .type = "num_lesser", + .func_new2 = func_new_lesser, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_greater", + .func_new2 = func_new_greater, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_lesser_equal", + .func_new2 = func_new_lesser_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_greater_equal", + .func_new2 = func_new_greater_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_equal", + .func_new2 = func_new_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_different", + .func_new2 = func_new_different, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_add", + .func_new2 = func_new_add, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_subtract", + .func_new2 = func_new_subtract, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_multiply", + .func_new2 = func_new_multiply, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_divide", + .func_new2 = func_new_divide, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_modulo", + .func_new2 = func_new_modulo, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_arithmetic = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/assert.c b/app/src/main/jni/badvpn/ncd/modules/assert.c new file mode 100644 index 0000000..75205c5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/assert.c @@ -0,0 +1,105 @@ +/** + * @file assert.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * assert(string cond) + * assert_false(string cond) + * + * Description: + * If 'cond' is equal to the string "true" (assert) or "false" (assert_false), + * does nothing. Otherwise, logs an error and initiates interpreter termination + * with exit code 1, i.e. it is equivalent to calling exit("1"). + * Note that "assert_false(cond);" is not completely equivalent to + * "not(cond) a; assert(a);", in case 'cond' is something other than "true" + * or "false". + */ + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new_common (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_false) +{ + // check arguments + NCDValRef cond_arg; + if (!NCDVal_ListRead(params->args, 1, &cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // if failed, initiate exit (before up!) + if ((!is_false && !NCDVal_StringEqualsId(cond_arg, NCD_STRING_TRUE, i->params->iparams->string_index)) || + (is_false && !NCDVal_StringEqualsId(cond_arg, NCD_STRING_FALSE, i->params->iparams->string_index)) + ) { + ModuleLog(i, BLOG_ERROR, "assertion failed"); + NCDModuleInst_Backend_InterpExit(i, 1); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(i, params, 0); +} + +static void func_new_false (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "assert", + .func_new2 = func_new + }, { + .type = "assert_false", + .func_new2 = func_new_false + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_assert = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/backtrack.c b/app/src/main/jni/badvpn/ncd/modules/backtrack.c new file mode 100644 index 0000000..ec9c5d7 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/backtrack.c @@ -0,0 +1,103 @@ +/** + * @file backtrack.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * backtrack_point() + * backtrack_point::go() + * + * Description: + * The backtrack_point() statement creates a backtrack point, going up immedietely. + * The go() method triggers backtracking to the backtrack point, i.e. makes the + * backtrack_point() statement go down and back up at atomically. The go() method + * itself goes up immedietely, but side effects of triggering backtracking have + * priority. + */ + +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void go_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get backtrack point + NCDModuleInst *backtrack_point_inst = params->method_user; + + // go up (after toggling) + NCDModuleInst_Backend_Up(i); + + // toggle backtrack point + NCDModuleInst_Backend_DownUp(backtrack_point_inst); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "backtrack_point", + .func_new2 = func_new + }, { + .type = "backtrack_point::go", + .func_new2 = go_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_backtrack = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/blocker.c b/app/src/main/jni/badvpn/ncd/modules/blocker.c new file mode 100644 index 0000000..b742d72 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/blocker.c @@ -0,0 +1,353 @@ +/** + * @file blocker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Blocker module. Provides a statement that blocks when initialized, and which can be blocked + * and unblocked from outside. + * + * Synopsis: blocker() + * Description: provides blocking operations. Initially the blocking state is down (but this statement + * does not block). On deinitialization, waits for all corresponding use() statements + * to die before dying itself. + * + * Synopsis: blocker::up() + * Description: sets the blocking state to up. + * The immediate effects of corresponding use() statements going up are processed before + * this statement goes up; but this statement statement still goes up immediately, + * assuming the effects mentioned haven't resulted in the intepreter scheduling this + * very statement for destruction. + * + * Synopsis: blocker::down() + * Description: sets the blocking state to down. + * The immediate effects of corresponding use() statements going up are processed before + * this statement goes up; but this statement statement still goes up immediately, + * assuming the effects mentioned haven't resulted in the intepreter scheduling this + * very statement for destruction. + * + * Synopsis: blocker::downup() + * Description: atomically sets the blocker to down state (if it was up), then (back) to up state. + * Note that this is not equivalent to calling down() and immediately up(); in that case, + * the interpreter will first handle the immediate effects of any use() statements + * going down as a result of having called down() and will only later execute the up() + * statement. In fact, it is possible that the effects of down() will prevent up() from + * executing, which may leave the program in an undesirable state. + * + * Synopsis: blocker::rdownup() + * Description: on deinitialization, atomically sets the blocker to down state (if it was up), then + * (back) to up state. + * The immediate effects of corresponding use() statements changing state are processed + * *after* the immediate effects of this statement dying (in contrast to downup()). + * + * Synopsis: blocker::use() + * Description: blocks on the blocker. This module is in up state if and only if the blocking state of + * the blocker is up. Multiple use statements may be used with the same blocker. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + LinkedList1 users; + LinkedList0 rdownups_list; + int up; + int dying; +}; + +struct rdownup_instance { + NCDModuleInst *i; + struct instance *blocker; + LinkedList0Node rdownups_list_node; +}; + +struct use_instance { + NCDModuleInst *i; + struct instance *blocker; + LinkedList1Node blocker_node; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init users list + LinkedList1_Init(&o->users); + + // init rdownups list + LinkedList0_Init(&o->rdownups_list); + + // set not up + o->up = 0; + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->users)) + + // break any rdownups + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->rdownups_list)) { + struct rdownup_instance *rdu = UPPER_OBJECT(ln, struct rdownup_instance, rdownups_list_node); + ASSERT(rdu->blocker == o) + LinkedList0_Remove(&o->rdownups_list, &rdu->rdownups_list_node); + rdu->blocker = NULL; + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // if we have no users, die right away, else wait for users + if (LinkedList1_IsEmpty(&o->users)) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; +} + +static void updown_func_new_templ (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int up, int first_down) +{ + ASSERT(!first_down || up) + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (first_down || mo->up != up) { + // signal users + for (LinkedList1Node *node = LinkedList1_GetFirst(&mo->users); node; node = LinkedList1Node_Next(node)) { + struct use_instance *user = UPPER_OBJECT(node, struct use_instance, blocker_node); + ASSERT(user->blocker == mo) + if (first_down && mo->up) { + NCDModuleInst_Backend_Down(user->i); + } + if (up) { + NCDModuleInst_Backend_Up(user->i); + } else { + NCDModuleInst_Backend_Down(user->i); + } + } + + // change up state + mo->up = up; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void up_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 1, 0); +} + +static void down_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 0, 0); +} + +static void downup_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 1, 1); +} + +static void rdownup_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct rdownup_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get blocker + struct instance *blk = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // set blocker + o->blocker = blk; + + // insert to rdownups list + LinkedList0_Prepend(&blk->rdownups_list, &o->rdownups_list_node); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rdownup_func_die (void *vo) +{ + struct rdownup_instance *o = vo; + + struct instance *blk = o->blocker; + + if (blk) { + // remove from rdownups list + LinkedList0_Remove(&blk->rdownups_list, &o->rdownups_list_node); + + // downup users + for (LinkedList1Node *ln = LinkedList1_GetFirst(&blk->users); ln; ln = LinkedList1Node_Next(ln)) { + struct use_instance *user = UPPER_OBJECT(ln, struct use_instance, blocker_node); + ASSERT(user->blocker == blk) + if (blk->up) { + NCDModuleInst_Backend_Down(user->i); + } + NCDModuleInst_Backend_Up(user->i); + } + + // set up + blk->up = 1; + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void use_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct use_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set blocker + o->blocker = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // add to blocker's list + LinkedList1_Append(&o->blocker->users, &o->blocker_node); + + // signal up if needed + if (o->blocker->up) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void use_func_die (void *vo) +{ + struct use_instance *o = vo; + + // remove from blocker's list + LinkedList1_Remove(&o->blocker->users, &o->blocker_node); + + // make the blocker die if needed + if (o->blocker->dying && LinkedList1_IsEmpty(&o->blocker->users)) { + instance_free(o->blocker); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "blocker", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "blocker::up", + .func_new2 = up_func_new + }, { + .type = "blocker::down", + .func_new2 = down_func_new + }, { + .type = "blocker::downup", + .func_new2 = downup_func_new + }, { + .type = "blocker::rdownup", + .func_new2 = rdownup_func_new, + .func_die = rdownup_func_die, + .alloc_size = sizeof(struct rdownup_instance) + }, { + .type = "blocker::use", + .func_new2 = use_func_new, + .func_die = use_func_die, + .alloc_size = sizeof(struct use_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_blocker = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/buffer.c b/app/src/main/jni/badvpn/ncd/modules/buffer.c new file mode 100644 index 0000000..eeb3715 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/buffer.c @@ -0,0 +1,619 @@ +/** + * @file buffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * buffer([string data]) + * + * Variables: + * string (empty) - data in the buffer + * string length - number of bytes in the buffer + * + * Description: + * Implements an array of bytes which supports appending bytes and removing + * bytes from the beginning. The buffer is implemented using chunks; + * the time complexity of operations depends on the number of chunks affected, + * and not on the actual number of bytes. Each append operation produces a single + * chunk. In particular: + * + * Complexity of append and construction: + * log(total number of chunks) + (time for copying data). + * Complexity of consume: + * log(total number of chunks) * (1 + (number of chunks in consumed range)) + * Complexity of referencing and unreferencing a range: + * log(total number of chunks) * (1 + (number of chunks in referenced range)) + * + * Synopsis: + * buffer::append(string data) + * + * Description: + * Appends the given data to the end of the buffer. + * + * Synopsis: + * buffer::consume(string amount) + * + * Description: + * Removes the specified number of bytes from the beginning of the buffer. + * 'amount' must not be larger than the current length of the buffer. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct chunk; + +#include "buffer_chunks_tree.h" +#include + +struct buffer { + struct instance *inst; + ChunksTree chunks_tree; + int refcnt; +}; + +struct chunk { + struct buffer *buf; + size_t offset; + size_t length; + ChunksTreeNode chunks_tree_node; + int refcnt; + char data[]; +}; + +struct reference { + struct chunk *first_chunk; + size_t first_offset; + size_t length; + BRefTarget ref_target; +}; + +struct instance { + NCDModuleInst *i; + size_t offset; + size_t total_length; + struct buffer *buf; +}; + +#include "buffer_chunks_tree.h" +#include + +static void instance_assert (struct instance *inst); +static int instance_append (struct instance *inst, NCDValRef string); +static void instance_consume (struct instance *inst, size_t amount); +static struct buffer * buffer_init (struct instance *inst, NCDModuleInst *i); +static void buffer_free (struct buffer *buf); +static void buffer_detach (struct buffer *buf); +static struct chunk * buffer_get_existing_chunk (struct buffer *buf, size_t offset); +static struct chunk * chunk_init (struct instance *inst, size_t length); +static void chunk_unref (struct chunk *c); +static void chunk_assert (struct chunk *c); +static struct reference * reference_init (struct instance *inst, size_t offset, size_t length, NCDValComposedStringResource *out_resource); +static void reference_ref_target_func_release (BRefTarget *ref_target); +static void reference_assert (struct reference *ref); +static void reference_resource_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length); + +static void instance_assert (struct instance *inst) +{ + ASSERT(inst->buf->inst == inst) +} + +static int instance_append (struct instance *inst, NCDValRef string) +{ + instance_assert(inst); + ASSERT(NCDVal_IsString(string)) + + size_t length = NCDVal_StringLength(string); + + // if string is empty do nothing, we can't make an empty chunk + if (length == 0) { + return 1; + } + + // init chunk + struct chunk *c = chunk_init(inst, length); + if (!c) { + return 0; + } + + // copy data to chunk + NCDVal_StringCopyOut(string, 0, length, c->data); + + return 1; +} + +static void instance_consume (struct instance *inst, size_t amount) +{ + instance_assert(inst); + ASSERT(amount <= inst->total_length - inst->offset) + + // nothing do to if amount is zero + if (amount == 0) { + return; + } + + // find chunk where the byte in the buffer resides + struct chunk *c = buffer_get_existing_chunk(inst->buf, inst->offset); + + // increment buffer offset + inst->offset += amount; + + // unreference chunks which no longer contain buffer contents + while (c && c->offset + c->length <= inst->offset) { + struct chunk *next_c = ChunksTree_GetNext(&inst->buf->chunks_tree, 0, c); + chunk_unref(c); + c = next_c; + } +} + +static struct buffer * buffer_init (struct instance *inst, NCDModuleInst *i) +{ + ASSERT(inst) + + // allocate structure + struct buffer *buf = BAlloc(sizeof(*buf)); + if (!buf) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + return NULL; + } + + // set instance pointer + buf->inst = inst; + + // init chunks tree + ChunksTree_Init(&buf->chunks_tree); + + // set refcnt to 0 (number of reference objects) + buf->refcnt = 0; + + return buf; +} + +static void buffer_free (struct buffer *buf) +{ + ASSERT(!buf->inst) + ASSERT(ChunksTree_IsEmpty(&buf->chunks_tree)) + ASSERT(buf->refcnt == 0) + + // free structure + BFree(buf); +} + +static void buffer_detach (struct buffer *buf) +{ + ASSERT(buf->inst) + struct instance *inst = buf->inst; + + // consume entire buffer to free any chunks that aren't referenced + instance_consume(inst, inst->total_length - inst->offset); + + // clear instance pointer + buf->inst = NULL; + + // free buffer if there are no more chunks + if (ChunksTree_IsEmpty(&buf->chunks_tree)) { + buffer_free(buf); + } +} + +static struct chunk * buffer_get_existing_chunk (struct buffer *buf, size_t offset) +{ + struct chunk *c = ChunksTree_GetLastLesserEqual(&buf->chunks_tree, 0, offset); + + ASSERT(c) + chunk_assert(c); + ASSERT(offset >= c->offset) + ASSERT(offset < c->offset + c->length) + + return c; +} + +static struct chunk * chunk_init (struct instance *inst, size_t length) +{ + instance_assert(inst); + ASSERT(length > 0) + struct buffer *buf = inst->buf; + + // make sure length is not too large + if (length >= SIZE_MAX - inst->total_length) { + ModuleLog(inst->i, BLOG_ERROR, "length overflow"); + return NULL; + } + + // allocate structure + bsize_t size = bsize_add(bsize_fromsize(sizeof(struct chunk)), bsize_fromsize(length)); + struct chunk *c = BAllocSize(size); + if (!c) { + ModuleLog(inst->i, BLOG_ERROR, "BAllocSize failed"); + return NULL; + } + + // set some members + c->buf = buf; + c->offset = inst->total_length; + c->length = length; + + // insert into chunks tree + int res = ChunksTree_Insert(&buf->chunks_tree, 0, c, NULL); + B_ASSERT_USE(res) + + // set reference count to 1 (referenced by buffer contents) + c->refcnt = 1; + + // increment buffer length + inst->total_length += length; + + chunk_assert(c); + return c; +} + +static void chunk_unref (struct chunk *c) +{ + chunk_assert(c); + + // decrement reference count + c->refcnt--; + + // if reference count is not yet zero, do nothing else + if (c->refcnt > 0) { + return; + } + + // remove from chunks tree + ChunksTree_Remove(&c->buf->chunks_tree, 0, c); + + // free structure + BFree(c); +} + +static void chunk_assert (struct chunk *c) +{ + ASSERT(c->buf) + ASSERT(c->length > 0) + ASSERT(!c->buf->inst || c->offset <= c->buf->inst->total_length) + ASSERT(!c->buf->inst || c->length <= c->buf->inst->total_length - c->offset) + ASSERT(c->refcnt > 0) +} + +static struct reference * reference_init (struct instance *inst, size_t offset, size_t length, NCDValComposedStringResource *out_resource) +{ + instance_assert(inst); + struct buffer *buf = inst->buf; + ASSERT(offset >= inst->offset) + ASSERT(offset <= inst->total_length) + ASSERT(length <= inst->total_length - offset) + ASSERT(length > 0) + ASSERT(out_resource) + + // check buffer reference count. This ensures we can always increment the + // chunk reference counts, below. We use (INT_MAX - 1) here because the buffer + // itself can also own references to chunks. + if (buf->refcnt == INT_MAX - 1) { + ModuleLog(inst->i, BLOG_ERROR, "too many references"); + return NULL; + } + + // allocate structure + struct reference *ref = BAlloc(sizeof(*ref)); + if (!ref) { + ModuleLog(inst->i, BLOG_ERROR, "BAlloc failed"); + return NULL; + } + + // find chunk where the first byte of the interval resides + struct chunk *c = buffer_get_existing_chunk(buf, offset); + + // set some members + ref->first_chunk = c; + ref->first_offset = offset - c->offset; + ref->length = length; + + // increment buffer reference count + buf->refcnt++; + + // reference chunks + do { + struct chunk *next_c = ChunksTree_GetNext(&buf->chunks_tree, 0, c); + ASSERT(c->refcnt < INT_MAX) + c->refcnt++; + c = next_c; + } while (c && c->offset < offset + length); + + // init reference target + BRefTarget_Init(&ref->ref_target, reference_ref_target_func_release); + + // write resource + out_resource->func_getptr = reference_resource_func_getptr; + out_resource->user = ref; + out_resource->ref_target = &ref->ref_target; + + reference_assert(ref); + return ref; +} + +static void reference_ref_target_func_release (BRefTarget *ref_target) +{ + struct reference *ref = UPPER_OBJECT(ref_target, struct reference, ref_target); + reference_assert(ref); + struct buffer *buf = ref->first_chunk->buf; + + // compute offset + size_t offset = ref->first_chunk->offset + ref->first_offset; + + // unreference chunks + struct chunk *c = ref->first_chunk; + do { + struct chunk *next_c = ChunksTree_GetNext(&buf->chunks_tree, 0, c); + chunk_unref(c); + c = next_c; + } while (c && c->offset < offset + ref->length); + + // decrement buffer reference count + ASSERT(buf->refcnt > 0) + buf->refcnt--; + + // free structure + BFree(ref); + + // if the instance has died and there are no more chunks, free buffer + if (!buf->inst && ChunksTree_IsEmpty(&buf->chunks_tree)) { + buffer_free(buf); + } +} + +static void reference_assert (struct reference *ref) +{ + ASSERT(ref->first_chunk) + ASSERT(ref->first_offset < ref->first_chunk->length) + ASSERT(ref->length > 0) + chunk_assert(ref->first_chunk); +} + +static void reference_resource_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length) +{ + struct reference *ref = user; + reference_assert(ref); + ASSERT(offset < ref->length) + ASSERT(out_data) + ASSERT(out_length) + + // compute absolute offset of request + size_t abs_offset = ref->first_chunk->offset + ref->first_offset + offset; + + // find chunk where the byte at the requested offset resides + struct chunk *c = buffer_get_existing_chunk(ref->first_chunk->buf, abs_offset); + + // compute offset of this byte within the chunk + size_t chunk_offset = abs_offset - c->offset; + + // return the data from this byte to the end of the chunk + *out_data = c->data + chunk_offset; + *out_length = c->length - chunk_offset; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // pass instance pointer to methods + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + NCDValRef data_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 0) && + !NCDVal_ListRead(params->args, 1, &data_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsInvalid(data_arg) && !NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // set offset and total length + o->offset = 0; + o->total_length = 0; + + // allocate buffer + o->buf = buffer_init(o, i); + if (!o->buf) { + goto fail0; + } + + // append initial data + if (!NCDVal_IsInvalid(data_arg)) { + if (!instance_append(o, data_arg)) { + goto fail1; + } + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + o->buf->inst = NULL; + buffer_free(o->buf); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_assert(o); + + // detach buffer from instance + buffer_detach(o->buf); + + // die + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + instance_assert(o); + + if (name == NCD_STRING_EMPTY) { + if (o->total_length - o->offset == 0) { + *out = NCDVal_NewStringUninitialized(mem, 0); + } else { + NCDValComposedStringResource resource; + struct reference *ref = reference_init(o, o->offset, o->total_length - o->offset, &resource); + if (!ref) { + goto fail; + } + *out = NCDVal_NewComposedString(mem, resource, 0, ref->length); + BRefTarget_Deref(resource.ref_target); + } + return 1; + } + + if (name == NCD_STRING_LENGTH) { + *out = ncd_make_uintmax(mem, o->total_length - o->offset); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void append_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get instance + struct instance *inst = params->method_user; + + // append + if (!instance_append(inst, data_arg)) { + ModuleLog(i, BLOG_ERROR, "instance_append failed"); + goto fail0; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void consume_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef amount_arg; + if (!NCDVal_ListRead(params->args, 1, &amount_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(amount_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse amount + uintmax_t amount; + if (!ncd_read_uintmax(amount_arg, &amount)) { + ModuleLog(i, BLOG_ERROR, "wrong amount"); + goto fail0; + } + + // get instance + struct instance *inst = params->method_user; + + // check amount + if (amount > inst->total_length - inst->offset) { + ModuleLog(i, BLOG_ERROR, "amount is more than buffer length"); + goto fail0; + } + + // consume + instance_consume(inst, amount); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "buffer", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "buffer::append", + .func_new2 = append_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "buffer::consume", + .func_new2 = consume_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_buffer = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/buffer_chunks_tree.h b/app/src/main/jni/badvpn/ncd/modules/buffer_chunks_tree.h new file mode 100644 index 0000000..c299d7c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/buffer_chunks_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME ChunksTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct chunk +#define SAVL_PARAM_TYPE_KEY size_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->offset, (entry2)->offset) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->offset) +#define SAVL_PARAM_MEMBER_NODE chunks_tree_node diff --git a/app/src/main/jni/badvpn/ncd/modules/call2.c b/app/src/main/jni/badvpn/ncd/modules/call2.c new file mode 100644 index 0000000..f3e65ba --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/call2.c @@ -0,0 +1,498 @@ +/** + * @file call2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * call(string template, list args) + * + * Description: + * Calls a process template. The 'template' argument is the name of the process + * template to call, and the 'list' argument is a list of arguments for the + * process template. Calling a process template is roughly equivalent to placing + * the statements within that template into the place of call(), except for the + * points presented next. The 'template' argument can be a special value "", + * which makes call() a no-op. + * + * The process created from the called template will be able to access the arguments + * that were given in the 'args' argument to call() via the '_argN' predefined\ + * objects (e.g. _arg0 for the first argumens), and also via '_args' for the entire + * argument list. + * + * The called process also will be able to access objects within the calling + * process as seen by the call() statement. However such any access needs to happen + * via a special '_caller' predefined object. For example, if there is a statement + * 'var("a") x;' somewhere above the call() statement, the called process can access + * it as '_caller.x'. + * + * Note that call() preserves backtracking semantics, i.e. when a statement within + * the called process goes down after having gone up, the behaviour really is as + * if the call() statement was replaced with the statements in the called template, + * (disregarding variable resolution). + * + * Because the template name is an argument, call() can be used for branching. + * For example, if we have an object 'x' with the value "true" or "false", a + * branch can be performed by defining two process templates, 'branch_true' + * and 'branch_false', and branching with the following code: + * + * concat("branch_", x) name; + * call(name, {}); + * + * Synopsis: + * call_with_caller_target(string template, list args, string caller_target) + * + * Description: + * Like call(), except that the target of the '_caller' predefined object is + * specified by the 'caller_target' argument. This is indented to be used from + * generic code for user-specified callbacks, allowing the user to easily refer to + * his own objects from inside the callback. + * + * The 'caller_target' must be a non-empty string referring to an actual object; + * there is no choice of 'caller_target' that would make call_with_caller_target() + * equivalent to call(). + * + * Synopsis: + * embcall2_multif(string cond1, string template1, ..., [string else_template]) + * + * Description: + * This is an internal command used to implement the 'If' clause. The arguments + * are pairs of (cond, template), where 'cond' is a condition in form of a string, + * and 'template' is the name of the process template for this condition. The + * template corresponding to the first condition equal to "true" is called; if + * there is no true condition, either the template 'else_template' is called, + * if it is provided, or nothing is performed, if 'else_template' is not provided. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_WORKING 1 +#define STATE_UP 2 +#define STATE_WAITING 3 +#define STATE_TERMINATING 4 +#define STATE_NONE 5 + +#define NUM_STATIC_NAMES 4 + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + int state; +}; + +struct instance_with_caller_target { + struct instance base; + NCD_string_id_t *dynamic_names; + size_t num_names; + NCD_string_id_t static_names[NUM_STATIC_NAMES]; +}; + +#define NAMES_PARAM_NAME CallNames +#define NAMES_PARAM_TYPE struct instance_with_caller_target +#define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +#define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +#define NAMES_PARAM_MEMBER_NUM_NAMES num_names +#define NAMES_PARAM_NUM_STATIC_NAMES NUM_STATIC_NAMES +#include + +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, int embed); +static void instance_free (struct instance *o); + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_WORKING) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = STATE_UP; + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_UP) + + // signal down + NCDModuleInst_Backend_Down(o->i); + + // set state waiting + o->state = STATE_WAITING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_TERMINATING) + + // die finally + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj_with_caller_target); + return 1; + } + + return 0; +} + +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance_with_caller_target *o_ch = NCDObject_DataPtr(obj); + ASSERT(o_ch->num_names > 0) + + NCD_string_id_t *names = CallNames_GetNames(o_ch); + + NCDObject object; + if (!NCDModuleInst_Backend_GetObj(o_ch->base.i, names[0], &object)) { + return 0; + } + + NCDObject obj2; + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, o_ch->num_names - 1, &obj2)) { + return 0; + } + + if (name == NCD_STRING_EMPTY) { + *out_object = obj2; + return 1; + } + + return NCDObject_GetObj(&obj2, name, out_object); +} + +static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, int embed) +{ + ASSERT(NCDVal_IsInvalid(template_name) || NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(embed == !!embed) + + struct instance *o = vo; + o->i = i; + + if (NCDVal_IsInvalid(template_name) || ncd_is_none(template_name)) { + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state none + o->state = STATE_NONE; + } else { + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special functions + if (embed) { + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_embed); + } else { + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_noembed); + } + + // set state working + o->state = STATE_WORKING; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + // free process + if (o->state != STATE_NONE) { + NCDModuleProcess_Free(&o->process); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_new_call (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef template_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_arg, &args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + func_new_templ(vo, i, template_arg, args_arg, 0); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_call_with_caller_target (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + struct instance_with_caller_target *o_ct = vo; + o->i = i; + + NCDValRef template_arg; + NCDValRef args_arg; + NCDValRef caller_target_arg; + if (!NCDVal_ListRead(params->args, 3, &template_arg, &args_arg, &caller_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg) || !NCDVal_IsString(caller_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValContString cts; + if (!NCDVal_StringContinuize(caller_target_arg, &cts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringContinuize failed"); + goto fail0; + } + + int res = CallNames_InitNames(o_ct, i->params->iparams->string_index, cts.data, NCDVal_StringLength(caller_target_arg)); + NCDValContString_Free(&cts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "CallerNames_InitNames failed"); + goto fail0; + } + + if (ncd_is_none(template_arg)) { + // signal up + NCDModuleInst_Backend_Up(i); + + // set state none + o->state = STATE_NONE; + } else { + // create process + if (!NCDModuleProcess_InitValue(&o->process, i, template_arg, args_arg, process_handler_event)) { + ModuleLog(i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail1; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_with_caller_target); + + // set state working + o->state = STATE_WORKING; + } + + return; + +fail1: + CallNames_FreeNames(o_ct); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_embcall_multif (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef args = params->args; + + NCDValRef template_value = NCDVal_NewInvalid(); + + size_t count = NCDVal_ListCount(args); + size_t j = 0; + + while (j < count) { + NCDValRef arg = NCDVal_ListGet(args, j); + + if (j == count - 1) { + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "bad arguments"); + goto fail0; + } + + template_value = arg; + break; + } + + NCDValRef arg2 = NCDVal_ListGet(args, j + 1); + + if (!NCDVal_IsString(arg) || !NCDVal_IsString(arg2)) { + ModuleLog(i, BLOG_ERROR, "bad arguments"); + goto fail0; + } + + if (ncd_read_boolean(arg)) { + template_value = arg2; + break; + } + + j += 2; + } + + func_new_templ(vo, i, template_value, NCDVal_NewInvalid(), 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_TERMINATING) + + // if none, die now + if (o->state == STATE_NONE) { + instance_free(o); + return; + } + + // request process to terminate + NCDModuleProcess_Terminate(&o->process); + + // set state terminating + o->state = STATE_TERMINATING; +} + +static void func_die_with_caller_target (void *vo) +{ + struct instance_with_caller_target *o_ct = vo; + + CallNames_FreeNames(o_ct); + + func_die(vo); +} + +static void func_clean (void *vo) +{ + struct instance *o = vo; + if (o->state != STATE_WAITING) { + return; + } + + // allow process to continue + NCDModuleProcess_Continue(&o->process); + + // set state working + o->state = STATE_WORKING; +} + +static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = vo; + + if (o->state == STATE_NONE) { + return 0; + } + + return NCDModuleProcess_GetObj(&o->process, name, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "call", + .func_new2 = func_new_call, + .func_die = func_die, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance) + }, { + .type = "call_with_caller_target", + .func_new2 = func_new_call_with_caller_target, + .func_die = func_die_with_caller_target, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance_with_caller_target) + }, { + .type = "embcall2_multif", + .func_new2 = func_new_embcall_multif, + .func_die = func_die, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_call2 = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/choose.c b/app/src/main/jni/badvpn/ncd/modules/choose.c new file mode 100644 index 0000000..23f8bbe --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/choose.c @@ -0,0 +1,145 @@ +/** + * @file choose.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Multiple value selection based on boolean conditions. + * + * Synopsis: + * choose({{string cond1, result1}, ..., {string condN, resultN}}, default_result) + * + * Variables: + * (empty) - If cond1="true" then result1, + * else if cond2="true" then result2, + * ..., + * else default_result. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValRef result; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_choices; + NCDValRef arg_default_result; + if (!NCDVal_ListRead(params->args, 2, &arg_choices, &arg_default_result)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(arg_choices)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // iterate choices + int have_result = 0; + size_t count = NCDVal_ListCount(arg_choices); + for (size_t j = 0; j < count; j++) { + NCDValRef c = NCDVal_ListGet(arg_choices, j); + + // check choice type + if (!NCDVal_IsList(c)) { + ModuleLog(i, BLOG_ERROR, "wrong choice type"); + goto fail0; + } + + // read choice + NCDValRef c_cond; + NCDValRef c_result; + if (!NCDVal_ListRead(c, 2, &c_cond, &c_result)) { + ModuleLog(i, BLOG_ERROR, "wrong choice contents arity"); + goto fail0; + } + if (!NCDVal_IsString(c_cond)) { + ModuleLog(i, BLOG_ERROR, "wrong choice condition type"); + goto fail0; + } + + // update result + if (!have_result && ncd_read_boolean(c_cond)) { + o->result = c_result; + have_result = 1; + } + } + + // default? + if (!have_result) { + o->result = arg_default_result; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->result); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "choose", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_choose = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/command_template.c b/app/src/main/jni/badvpn/ncd/modules/command_template.c new file mode 100644 index 0000000..6eb92f6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/command_template.c @@ -0,0 +1,218 @@ +/** + * @file command_template.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#define STATE_ADDING_LOCK 1 +#define STATE_ADDING 2 +#define STATE_ADDING_NEED_DELETE 3 +#define STATE_DONE 4 +#define STATE_DELETING_LOCK 5 +#define STATE_DELETING 6 + +static void lock_handler (command_template_instance *o); +static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status); +static void free_template (command_template_instance *o, int is_error); + +static void lock_handler (command_template_instance *o) +{ + ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_DELETING_LOCK) + ASSERT(!(o->state == STATE_ADDING_LOCK) || o->do_exec) + ASSERT(!(o->state == STATE_DELETING_LOCK) || o->undo_exec) + + if (o->state == STATE_ADDING_LOCK) { + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->do_exec, CmdLine_Get(&o->do_cmdline), NULL)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); + free_template(o, 1); + return; + } + + // set state + o->state = STATE_ADDING; + } else { + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->undo_exec, CmdLine_Get(&o->undo_cmdline), NULL)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); + free_template(o, 1); + return; + } + + // set state + o->state = STATE_DELETING; + } +} + +static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_ADDING || o->state == STATE_ADDING_NEED_DELETE || o->state == STATE_DELETING) + + // release lock + BEventLockJob_Release(&o->elock_job); + + // free process + BProcess_Free(&o->process); + + if (!normally || normally_exit_status != 0) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "command failed"); + + free_template(o, 1); + return; + } + + switch (o->state) { + case STATE_ADDING: { + // set state + o->state = STATE_DONE; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } break; + + case STATE_ADDING_NEED_DELETE: { + if (o->undo_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_DELETING_LOCK; + } else { + free_template(o, 0); + return; + } + } break; + + case STATE_DELETING: { + // finish + free_template(o, 0); + return; + } break; + } +} + +void command_template_new (command_template_instance *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock) +{ + // init arguments + o->i = i; + o->free_func = free_func; + o->user = user; + o->blog_channel = blog_channel; + + // build do command + if (!build_cmdline(o->i, params->args, 0, &o->do_exec, &o->do_cmdline)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline do callback failed"); + goto fail0; + } + + // build undo command + if (!build_cmdline(o->i, params->args, 1, &o->undo_exec, &o->undo_cmdline)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline undo callback failed"); + goto fail1; + } + + // init lock job + BEventLockJob_Init(&o->elock_job, elock, (BEventLock_handler)lock_handler, o); + + if (o->do_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_ADDING_LOCK; + } else { + // set state + o->state = STATE_DONE; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail1: + if (o->do_exec) { + free(o->do_exec); + CmdLine_Free(&o->do_cmdline); + } +fail0: + o->free_func(o->user, 1); +} + +static void free_template (command_template_instance *o, int is_error) +{ + // free lock job + BEventLockJob_Free(&o->elock_job); + + // free undo command + if (o->undo_exec) { + free(o->undo_exec); + CmdLine_Free(&o->undo_cmdline); + } + + // free do command + if (o->do_exec) { + free(o->do_exec); + CmdLine_Free(&o->do_cmdline); + } + + // call free function + o->free_func(o->user, is_error); +} + +void command_template_die (command_template_instance *o) +{ + ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_ADDING || o->state == STATE_DONE) + + switch (o->state) { + case STATE_ADDING_LOCK: { + free_template(o, 0); + return; + } break; + + case STATE_ADDING: { + // set state + o->state = STATE_ADDING_NEED_DELETE; + } break; + + case STATE_DONE: { + if (o->undo_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_DELETING_LOCK; + } else { + free_template(o, 0); + return; + } + } break; + } +} diff --git a/app/src/main/jni/badvpn/ncd/modules/command_template.h b/app/src/main/jni/badvpn/ncd/modules/command_template.h new file mode 100644 index 0000000..e1228ae --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/command_template.h @@ -0,0 +1,62 @@ +/** + * @file command_template.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Template for a module which executes a command to start and stop. + * The command is executed asynchronously. + */ + +#ifndef BADVPN_NCD_MODULES_COMMAND_TEMPLATE_H +#define BADVPN_NCD_MODULES_COMMAND_TEMPLATE_H + +#include +#include +#include + +typedef int (*command_template_build_cmdline) (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl); +typedef void (*command_template_free_func) (void *user, int is_error); + +typedef struct { + NCDModuleInst *i; + command_template_free_func free_func; + void *user; + int blog_channel; + char *do_exec; + CmdLine do_cmdline; + char *undo_exec; + CmdLine undo_cmdline; + BEventLockJob elock_job; + int state; + BProcess process; +} command_template_instance; + +void command_template_new (command_template_instance *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock); +void command_template_die (command_template_instance *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/modules/concat.c b/app/src/main/jni/badvpn/ncd/modules/concat.c new file mode 100644 index 0000000..e71e69e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/concat.c @@ -0,0 +1,189 @@ +/** + * @file concat.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * concat([string elem ...]) + * concatv(list strings) + * + * Description: + * Concatenates zero or more strings. The result is available as the empty + * string variable. For concatv(), the strings are provided as a single + * list argument. For concat(), the strings are provided as arguments + * themselves. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct result { + BRefTarget ref_target; + size_t length; + char data[]; +}; + +struct instance { + NCDModuleInst *i; + struct result *result; +}; + +static void result_ref_target_func_release (BRefTarget *ref_target) +{ + struct result *result = UPPER_OBJECT(ref_target, struct result, ref_target); + + BFree(result); +} + +static void new_concat_common (void *vo, NCDModuleInst *i, NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + struct instance *o = vo; + o->i = i; + + size_t count = NCDVal_ListCount(list); + bsize_t result_size = bsize_fromsize(sizeof(struct result)); + + // check arguments and compute result size + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + result_size = bsize_add(result_size, bsize_fromsize(NCDVal_StringLength(arg))); + } + + // allocate result + o->result = BAllocSize(result_size); + if (!o->result) { + ModuleLog(i, BLOG_ERROR, "BAllocSize failed"); + goto fail0; + } + + // init ref target + BRefTarget_Init(&o->result->ref_target, result_ref_target_func_release); + + // copy data to result + o->result->length = 0; + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + b_cstring cstr = NCDVal_StringCstring(arg); + b_cstring_copy_to_buf(cstr, 0, cstr.length, o->result->data + o->result->length); + o->result->length += cstr.length; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_concat (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_concat_common(vo, i, params->args); +} + +static void func_new_concatv (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef list_arg; + if (!NCDVal_ListRead(params->args, 1, &list_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(list_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + new_concat_common(vo, i, list_arg); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // release result reference + BRefTarget_Deref(&o->result->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, o->result->data, o->result->length, &o->result->ref_target); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "concat", + .func_new2 = func_new_concat, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "concatv", + .func_new2 = func_new_concatv, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_concat = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/daemon.c b/app/src/main/jni/badvpn/ncd/modules/daemon.c new file mode 100644 index 0000000..3dac4c5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/daemon.c @@ -0,0 +1,296 @@ +/** + * @file daemon.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Runs a program in the background, restarting it if it crashes. + * On deinitialization, sends SIGTERM to the daemon and waits for it to terminate + * (unless it's crashed at the time). + * + * Synopsis: + * daemon(list(string) cmd [, map options]) + * + * Arguments: + * cmd - Command for the daemon. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). + * options - Map of options: + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define RETRY_TIME 10000 + +#define STATE_RETRYING 1 +#define STATE_RUNNING 2 +#define STATE_RUNNING_DIE 3 + +struct instance { + NCDModuleInst *i; + NCDValRef cmd_arg; + NCDBProcessOpts opts; + BTimer timer; + BProcess process; + int state; +}; + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl); +static void start_process (struct instance *o); +static void timer_handler (struct instance *o); +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status); +static void instance_free (struct instance *o); + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + + if (!NCDVal_IsList(cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + ModuleLog(i, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, cstr, 0, cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void start_process (struct instance *o) +{ + // build cmdline + char *exec; + CmdLine cl; + if (!build_cmdline(o->i, o->cmd_arg, &exec, &cl)) { + goto fail; + } + + // start process + struct BProcess_params p_params = NCDBProcessOpts_GetParams(&o->opts); + int res = BProcess_Init2(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, exec, CmdLine_Get(&cl), p_params); + CmdLine_Free(&cl); + free(exec); + + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BProcess_Init2 failed"); + goto fail; + } + + // set state running + o->state = STATE_RUNNING; + return; + +fail: + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); + + // set state retrying + o->state = STATE_RETRYING; +} + +static void timer_handler (struct instance *o) +{ + ASSERT(o->state == STATE_RETRYING) + + ModuleLog(o->i, BLOG_INFO, "restarting after crash"); + + start_process(o); +} + +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_RUNNING || o->state == STATE_RUNNING_DIE) + + // free process + BProcess_Free(&o->process); + + // if we were requested to die, die now + if (o->state == STATE_RUNNING_DIE) { + instance_free(o); + return; + } + + BLog(BLOG_ERROR, "daemon crashed"); + + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); + + // set state retrying + o->state = STATE_RETRYING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &o->cmd_arg) && + !NCDVal_ListRead(params->args, 2, &o->cmd_arg, &opts_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init options + if (!NCDBProcessOpts_Init(&o->opts, opts_arg, NULL, NULL, i, BLOG_CURRENT_CHANNEL)) { + goto fail0; + } + + // init timer + BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o); + + // signal up + NCDModuleInst_Backend_Up(i); + + // try starting process + start_process(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + // free options + NCDBProcessOpts_Free(&o->opts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_RUNNING_DIE) + + // if not running, die immediately + if (o->state == STATE_RETRYING) { + instance_free(o); + return; + } + + // request termination + BProcess_Terminate(&o->process); + + // set state running die + o->state = STATE_RUNNING_DIE; +} + +static struct NCDModule modules[] = { + { + .type = "daemon", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_daemon = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/depend.c b/app/src/main/jni/badvpn/ncd/modules/depend.c new file mode 100644 index 0000000..5019cf4 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/depend.c @@ -0,0 +1,452 @@ +/** + * @file depend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dependencies module. + * + * Synopsis: provide(string name) + * Description: Provides a resource. On initialization, transitions any depend()-s + * waiting for this resource to UP state. On deinitialization, transitions + * depend()-s using this resource to DOWN state, and waits for all of them to + * receive the clean signal (i.e. wait for all of the statements following them in + * their processes to terminate). Initialization fails if a provide() already + * exists for this resource (including if it is being deinitialized). + * + * Synopsis: provide_event(string name) + * Description: Like provide(), but if another provide() already exists for this + * resource, initialization does not fail, and the request is queued to the active + * provide() for this resource. When an active provide() disappears that has + * queued provide()-s, one of them is promoted to be the active provide() for this + * resource, and the remaining queue is transferred to it. + * (mentions of provide() in this text also apply to provide_event()) + * + * Synopsis: depend(string name) + * Description: Depends on a resource. Is in UP state when a provide() + * for this resource is available, and in DOWN state when it is not (either + * it does not exist or is being terminated). + * Variables: Provides variables available from the corresponding provide, + * ("modname.varname" or "modname"). + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide { + NCDModuleInst *i; + const char *name; + size_t name_len; + int is_queued; + union { + struct { + LinkedList3Node queued_node; // node in list which begins with provide.queued_provides_firstnode + }; + struct { + LinkedList1Node provides_node; // node in provides + LinkedList1 depends; + LinkedList3Node queued_provides_firstnode; + int dying; + }; + }; +}; + +struct depend { + NCDModuleInst *i; + const char *name; + size_t name_len; + struct provide *p; + LinkedList1Node node; +}; + +struct global { + LinkedList1 provides; + LinkedList1 free_depends; +}; + +static struct provide * find_provide (struct global *g, const char *name, size_t name_len) +{ + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->provides); n; n = LinkedList1Node_Next(n)) { + struct provide *p = UPPER_OBJECT(n, struct provide, provides_node); + ASSERT(!p->is_queued) + + if (p->name_len == name_len && !memcmp(p->name, name, name_len)) { + return p; + } + } + + return NULL; +} + +static void provide_promote (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(!find_provide(g, o->name, o->name_len)) + + // set not queued + o->is_queued = 0; + + // insert to provides list + LinkedList1_Append(&g->provides, &o->provides_node); + + // init depends list + LinkedList1_Init(&o->depends); + + // set not dying + o->dying = 0; + + // attach free depends with this name + LinkedList1Node *n = LinkedList1_GetFirst(&g->free_depends); + while (n) { + LinkedList1Node *next = LinkedList1Node_Next(n); + struct depend *d = UPPER_OBJECT(n, struct depend, node); + ASSERT(!d->p) + + if (d->name_len != o->name_len || memcmp(d->name, o->name, d->name_len)) { + n = next; + continue; + } + + // remove from free depends list + LinkedList1_Remove(&g->free_depends, &d->node); + + // insert to provide's list + LinkedList1_Append(&o->depends, &d->node); + + // set provide + d->p = o; + + // signal up + NCDModuleInst_Backend_Up(d->i); + + n = next; + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init provides list + LinkedList1_Init(&g->provides); + + // init free depends list + LinkedList1_Init(&g->free_depends); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->free_depends)) + ASSERT(LinkedList1_IsEmpty(&g->provides)) + + // free global state structure + BFree(g); +} + +static void provide_func_new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int event) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->name = NCDVal_StringData(name_arg); + o->name_len = NCDVal_StringLength(name_arg); + + // signal up. + // This comes above provide_promote(), so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // check for existing provide with this name + struct provide *ep = find_provide(g, o->name, o->name_len); + if (ep) { + ASSERT(!ep->is_queued) + + if (!event) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // set queued + o->is_queued = 1; + + // insert to existing provide's queued provides list + LinkedList3Node_InitAfter(&o->queued_node, &ep->queued_provides_firstnode); + } else { + // init first node for queued provides list + LinkedList3Node_InitLonely(&o->queued_provides_firstnode); + + // promote provide + provide_promote(o); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + provide_func_new_templ(vo, i, params, 0); +} + +static void provide_event_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + provide_func_new_templ(vo, i, params, 1); +} + +static void provide_free (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(o->is_queued || LinkedList1_IsEmpty(&o->depends)) + + if (o->is_queued) { + // remove from existing provide's queued provides list + LinkedList3Node_Free(&o->queued_node); + } else { + // remove from provides list + LinkedList1_Remove(&g->provides, &o->provides_node); + + // if we have provides queued, promote the first one + if (LinkedList3Node_Next(&o->queued_provides_firstnode)) { + // get first queued provide + struct provide *qp = UPPER_OBJECT(LinkedList3Node_Next(&o->queued_provides_firstnode), struct provide, queued_node); + ASSERT(qp->is_queued) + + // make it the head of the queued provides list + LinkedList3Node_Free(&qp->queued_node); + LinkedList3Node_InitAfter(&qp->queued_provides_firstnode, &o->queued_provides_firstnode); + LinkedList3Node_Free(&o->queued_provides_firstnode); + + // promote provide + provide_promote(qp); + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(o->is_queued || !o->dying) + + // if we are queued or have no depends, die immediately + if (o->is_queued || LinkedList1_IsEmpty(&o->depends)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // signal our depends down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->depends); n; n = LinkedList1Node_Next(n)) { + struct depend *d = UPPER_OBJECT(n, struct depend, node); + ASSERT(d->p == o) + + // signal down + NCDModuleInst_Backend_Down(d->i); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->name = NCDVal_StringData(name_arg); + o->name_len = NCDVal_StringLength(name_arg); + + // find a provide with our name + struct provide *p = find_provide(g, o->name, o->name_len); + ASSERT(!p || !p->is_queued) + + if (p && !p->dying) { + // insert to provide's list + LinkedList1_Append(&p->depends, &o->node); + + // set provide + o->p = p; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } else { + // insert to free depends list + LinkedList1_Append(&g->free_depends, &o->node); + + // set no provide + o->p = NULL; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_free (struct depend *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(!o->p || !o->p->is_queued) + + if (o->p) { + // remove from provide's list + LinkedList1_Remove(&o->p->depends, &o->node); + + // if provide is dying and is empty, let it die + if (o->p->dying && LinkedList1_IsEmpty(&o->p->depends)) { + provide_free(o->p); + } + } else { + // remove free depends list + LinkedList1_Remove(&g->free_depends, &o->node); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + + depend_free(o); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + struct global *g = ModuleGlobal(o->i); + ASSERT(!o->p || !o->p->is_queued) + + if (!(o->p && o->p->dying)) { + return; + } + + struct provide *p = o->p; + + // remove from provide's list + LinkedList1_Remove(&p->depends, &o->node); + + // insert to free depends list + LinkedList1_Append(&g->free_depends, &o->node); + + // set no provide + o->p = NULL; + + // if provide is empty, let it die + if (LinkedList1_IsEmpty(&p->depends)) { + provide_free(p); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + ASSERT(!o->p || !o->p->is_queued) + + if (!o->p) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->p->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "provide_event", + .func_new2 = provide_event_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_depend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/depend_scope.c b/app/src/main/jni/badvpn/ncd/modules/depend_scope.c new file mode 100644 index 0000000..1a814a0 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/depend_scope.c @@ -0,0 +1,466 @@ +/** + * @file depend_scope.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Multiple-option dependencies module. + * + * Synopsis: + * depend_scope() + * + * Description: + * A scope for dependency names. Any dependency names used in provide() and depend() + * methods on this object are local to this object. Contrast to the multidepend module, + * which provides the same functionality as this module, but with a single global + * dependency name scope. + * + * Synopsis: + * depend_scope::provide(name) + * + * Arguments: + * name - provider identifier + * + * Description: + * Satisfies a dependency. + * If any depend()'s get immediately bound to this provide(), + * the side effects of those first happen, and only then can the process of this + * provide() continue. + * When provide() is requested to deinitialize, if there are any depend()'s bound, + * provide() will not finish deinitializing until all the processes containing the + * bound depend()'s have backtracked to the point of the corresponding depend(). + * More specifically, when backtracking has finished for the last bound depend(), + * first the immediate effects of the provide() finshing deinitialization will happen, + * and only then will the depend() attempt to rebind. (If the converse was true, the + * depend() could rebind, but when deinitialization of the provide()'s process + * continues, lose this binding. See ncd/tests/depend_scope.ncd .) + * + * Synopsis: + * depend_scope::depend(list names) + * + * Arguments: + * names - list of provider identifiers. Names more to the beginning are considered + * more desirable. + * + * Description: + * Binds to the provide() providing one of the specified dependency names which is most + * desirable. If there is no provide() providing any of the given dependency names, + * waits and binds when one becomes available. + * If the depend() is bound to a provide(), and the bound provide() is requested to + * deinitize, or a more desirable provide() becomes available, the depend() statement + * will go down (triggering backtracking), wait for backtracking to finish, and then + * try to bind to a provide() again, as if it was just initialized. + * When depend() is requested to deinitialize, it deinitializes immediately. + * + * Attributes: + * Exposes objects as seen from the corresponding provide. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct scope { + BRefTarget ref_target; + LinkedList1 provides_list; + LinkedList1 depends_list; +}; + +struct scope_instance { + NCDModuleInst *i; + struct scope *scope; +}; + +struct provide { + NCDModuleInst *i; + struct scope *scope; + NCDValRef name; + LinkedList1Node provides_list_node; + LinkedList1 depends_list; + int dying; +}; + +struct depend { + NCDModuleInst *i; + struct scope *scope; + NCDValRef names; + LinkedList1Node depends_list_node; + struct provide *provide; + LinkedList1Node provide_depends_list_node; + int provide_collapsing; +}; + +static struct provide * find_provide (struct scope *o, NCDValRef name) +{ + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->provides_list); ln; ln = LinkedList1Node_Next(ln)) { + struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node); + ASSERT(provide->scope == o) + if (NCDVal_Compare(provide->name, name) == 0) { + return provide; + } + } + + return NULL; +} + +static struct provide * depend_find_best_provide (struct depend *o) +{ + size_t count = NCDVal_ListCount(o->names); + + for (size_t j = 0; j < count; j++) { + NCDValRef name = NCDVal_ListGet(o->names, j); + struct provide *provide = find_provide(o->scope, name); + if (provide && !provide->dying) { + return provide; + } + } + + return NULL; +} + +static void depend_update (struct depend *o) +{ + // if we're collapsing, do nothing + if (o->provide && o->provide_collapsing) { + return; + } + + // find best provide + struct provide *best_provide = depend_find_best_provide(o); + ASSERT(!best_provide || !best_provide->dying) + + // has anything changed? + if (best_provide == o->provide) { + return; + } + + if (o->provide) { + // set collapsing + o->provide_collapsing = 1; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + // insert to provide's list + LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node); + + // set not collapsing + o->provide_collapsing = 0; + + // set provide + o->provide = best_provide; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } +} + +static void scope_ref_target_func_release (BRefTarget *ref_target) +{ + struct scope *o = UPPER_OBJECT(ref_target, struct scope, ref_target); + ASSERT(LinkedList1_IsEmpty(&o->provides_list)) + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + BFree(o); +} + +static void scope_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct scope_instance *o = vo; + o->i = i; + + // pass scope instance pointer to methods not NCDModuleInst pointer + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // allocate scope + o->scope = BAlloc(sizeof(*o->scope)); + if (!o->scope) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + // init reference target + BRefTarget_Init(&o->scope->ref_target, scope_ref_target_func_release); + + // init provide and depend lists + LinkedList1_Init(&o->scope->provides_list); + LinkedList1_Init(&o->scope->depends_list); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void scope_func_die (void *vo) +{ + struct scope_instance *o = vo; + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct provide *o = vo; + o->i = i; + o->scope = ((struct scope_instance *)params->method_user)->scope; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // remember name + o->name = name_arg; + + // check for existing provide with this name + if (find_provide(o->scope, o->name)) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // grab scope reference + if (!BRefTarget_Ref(&o->scope->ref_target)) { + ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail0; + } + + // insert to provides list + LinkedList1_Append(&o->scope->provides_list, &o->provides_list_node); + + // init depends list + LinkedList1_Init(&o->depends_list); + + // set not dying + o->dying = 0; + + // signal up. + // This comes above the loop which follows, so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // update depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->scope->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node); + depend_update(depend); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + // remove from provides list + LinkedList1_Remove(&o->scope->provides_list, &o->provides_list_node); + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(!o->dying) + + // if we have no depends, die immediately + if (LinkedList1_IsEmpty(&o->depends_list)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // start collapsing our depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node); + ASSERT(depend->provide == o) + + // update depend to make sure it is collapsing + depend_update(depend); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct depend *o = vo; + o->i = i; + o->scope = ((struct scope_instance *)params->method_user)->scope; + + // read arguments + NCDValRef names_arg; + if (!NCDVal_ListRead(params->args, 1, &names_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(names_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // remember names + o->names = names_arg; + + // grab scope reference + if (!BRefTarget_Ref(&o->scope->ref_target)) { + ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail0; + } + + // insert to depends list + LinkedList1_Append(&o->scope->depends_list, &o->depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + + if (o->provide) { + // remove from provide's list + LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node); + + // if provide is dying and is empty, let it die + if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) { + provide_free(o->provide); + } + } + + // remove from depends list + LinkedList1_Remove(&o->scope->depends_list, &o->depends_list_node); + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + + if (!(o->provide && o->provide_collapsing)) { + return; + } + + // save provide + struct provide *provide = o->provide; + + // remove from provide's list + LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + + // if provide is dying and is empty, let it die. + // This comes after depend_update so that the side effects of the + // provide dying have priority over rebinding the depend. + if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) { + provide_free(provide); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + + if (!o->provide) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "depend_scope", + .func_new2 = scope_func_new, + .func_die = scope_func_die, + .alloc_size = sizeof(struct scope_instance) + }, { + .type = "depend_scope::provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "depend_scope::depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_depend_scope = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/dynamic_depend.c b/app/src/main/jni/badvpn/ncd/modules/dynamic_depend.c new file mode 100644 index 0000000..1fc747a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/dynamic_depend.c @@ -0,0 +1,548 @@ +/** + * @file dynamic_depend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dynamic dependencies module. + * + * Synopsis: dynamic_provide(string name, order_value) + * Synopsis: dynamic_depend(string name) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide; + +struct name_string { + char *data; + size_t len; +}; + +struct name { + struct global *g; + struct name_string name; + BAVLNode names_tree_node; + BAVL provides_tree; + LinkedList0 waiting_depends_list; + struct provide *cur_p; + LinkedList0 cur_bound_depends_list; + int cur_resetting; +}; + +struct provide { + NCDModuleInst *i; + struct name *n; + NCDValRef order_value; + BAVLNode provides_tree_node; + int dying; +}; + +struct depend { + NCDModuleInst *i; + struct name *n; + int is_bound; + LinkedList0Node depends_list_node; +}; + +struct global { + BAVL names_tree; +}; + +static void provide_free (struct provide *o); +static void depend_free (struct depend *o); + +static int name_string_comparator (void *user, void *vv1, void *vv2) +{ + struct name_string *v1 = vv1; + struct name_string *v2 = vv2; + + size_t min_len = (v1->len < v2->len ? v1->len : v2->len); + + int cmp = memcmp(v1->data, v2->data, min_len); + if (cmp) { + return B_COMPARE(cmp, 0); + } + + return B_COMPARE(v1->len, v2->len); +} + +static int val_comparator (void *user, NCDValRef *v1, NCDValRef *v2) +{ + return NCDVal_Compare(*v1, *v2); +} + +static struct name * find_name (struct global *g, const char *name, size_t name_len) +{ + struct name_string ns = {(char *)name, name_len}; + BAVLNode *tn = BAVL_LookupExact(&g->names_tree, &ns); + if (!tn) { + return NULL; + } + + struct name *n = UPPER_OBJECT(tn, struct name, names_tree_node); + ASSERT(n->name.len == name_len) + ASSERT(!memcmp(n->name.data, name, name_len)) + + return n; +} + +static struct name * name_init (NCDModuleInst *i, struct global *g, const char *name, size_t name_len) +{ + ASSERT(!find_name(g, name, name_len)) + + // allocate structure + struct name *o = malloc(sizeof(*o)); + if (!o) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set global state + o->g = g; + + // copy name + if (!(o->name.data = b_strdup_bin(name, name_len))) { + ModuleLog(i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + o->name.len = name_len; + + // insert to names tree + ASSERT_EXECUTE(BAVL_Insert(&g->names_tree, &o->names_tree_node, NULL)) + + // init provides tree + BAVL_Init(&o->provides_tree, OFFSET_DIFF(struct provide, order_value, provides_tree_node), (BAVL_comparator)val_comparator, NULL); + + // init waiting depends list + LinkedList0_Init(&o->waiting_depends_list); + + // set no current provide + o->cur_p = NULL; + + return o; + +fail1: + free(o); +fail0: + return NULL; +} + +static void name_free (struct name *o) +{ + ASSERT(BAVL_IsEmpty(&o->provides_tree)) + ASSERT(LinkedList0_IsEmpty(&o->waiting_depends_list)) + ASSERT(!o->cur_p) + + // remove from names tree + BAVL_Remove(&o->g->names_tree, &o->names_tree_node); + + // free name + free(o->name.data); + + // free structure + free(o); +} + +static struct provide * name_get_first_provide (struct name *o) +{ + BAVLNode *tn = BAVL_GetFirst(&o->provides_tree); + if (!tn) { + return NULL; + } + + struct provide *p = UPPER_OBJECT(tn, struct provide, provides_tree_node); + ASSERT(p->n == o) + + return p; +} + +static void name_new_current (struct name *o) +{ + ASSERT(!o->cur_p) + ASSERT(!BAVL_IsEmpty(&o->provides_tree)) + + // set current provide + o->cur_p = name_get_first_provide(o); + + // init bound depends list + LinkedList0_Init(&o->cur_bound_depends_list); + + // set not resetting + o->cur_resetting = 0; + + // bind waiting depends + while (!LinkedList0_IsEmpty(&o->waiting_depends_list)) { + struct depend *d = UPPER_OBJECT(LinkedList0_GetFirst(&o->waiting_depends_list), struct depend, depends_list_node); + ASSERT(d->n == o) + ASSERT(!d->is_bound) + + // remove from waiting depends list + LinkedList0_Remove(&o->waiting_depends_list, &d->depends_list_node); + + // set bound + d->is_bound = 1; + + // add to bound depends list + LinkedList0_Prepend(&o->cur_bound_depends_list, &d->depends_list_node); + + // signal up + NCDModuleInst_Backend_Up(d->i); + } +} + +static void name_free_if_unused (struct name *o) +{ + if (BAVL_IsEmpty(&o->provides_tree) && LinkedList0_IsEmpty(&o->waiting_depends_list)) { + name_free(o); + } +} + +static void name_continue_resetting (struct name *o) +{ + ASSERT(o->cur_p) + ASSERT(o->cur_resetting) + + // still have bound depends? + if (!LinkedList0_IsEmpty(&o->cur_bound_depends_list)) { + return; + } + + struct provide *old_p = o->cur_p; + + // set no current provide + o->cur_p = NULL; + + // free old current provide if it's dying + if (old_p->dying) { + provide_free(old_p); + } + + if (!BAVL_IsEmpty(&o->provides_tree)) { + // get new current provide + name_new_current(o); + } else { + // free name if unused + name_free_if_unused(o); + } +} + +static void name_start_resetting (struct name *o) +{ + ASSERT(o->cur_p) + ASSERT(!o->cur_resetting) + + // set resetting + o->cur_resetting = 1; + + // signal bound depends down + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->cur_bound_depends_list); ln; ln = LinkedList0Node_Next(ln)) { + struct depend *d = UPPER_OBJECT(ln, struct depend, depends_list_node); + ASSERT(d->n == o) + ASSERT(d->is_bound) + NCDModuleInst_Backend_Down(d->i); + } + + // if there were no bound depends, continue right away + name_continue_resetting(o); +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init names tree + BAVL_Init(&g->names_tree, OFFSET_DIFF(struct name, name, names_tree_node), name_string_comparator, NULL); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(BAVL_IsEmpty(&g->names_tree)) + + // free global state structure + BFree(g); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 2, &name_arg, &o->order_value)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *name_str = NCDVal_StringData(name_arg); + size_t name_len = NCDVal_StringLength(name_arg); + + // find name, create new if needed + struct name *n = find_name(g, name_str, name_len); + if (!n && !(n = name_init(i, g, name_str, name_len))) { + goto fail0; + } + + // set name + o->n = n; + + // check for order value conflict + if (BAVL_LookupExact(&n->provides_tree, &o->order_value)) { + ModuleLog(i, BLOG_ERROR, "order value already exists"); + goto fail0; + } + + // add to name's provides tree + ASSERT_EXECUTE(BAVL_Insert(&n->provides_tree, &o->provides_tree_node, NULL)) + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + + // should this be the current provide? + if (o == name_get_first_provide(n)) { + if (!n->cur_p) { + name_new_current(n); + } + else if (!n->cur_resetting) { + name_start_resetting(n); + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + struct name *n = o->n; + ASSERT(o->dying) + ASSERT(o != n->cur_p) + + // remove from name's provides tree + BAVL_Remove(&n->provides_tree, &o->provides_tree_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + struct name *n = o->n; + ASSERT(!o->dying) + + // set dying + o->dying = 1; + + // if this is not the current provide, die right away + if (o != n->cur_p) { + // free provide + provide_free(o); + + // free name if unused + name_free_if_unused(n); + return; + } + + ASSERT(!n->cur_resetting) + + // start resetting + name_start_resetting(n); +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *name_str = NCDVal_StringData(name_arg); + size_t name_len = NCDVal_StringLength(name_arg); + + // find name, create new if needed + struct name *n = find_name(g, name_str, name_len); + if (!n && !(n = name_init(i, g, name_str, name_len))) { + goto fail0; + } + + // set name + o->n = n; + + if (n->cur_p && !n->cur_resetting) { + // set bound + o->is_bound = 1; + + // add to bound depends list + LinkedList0_Prepend(&n->cur_bound_depends_list, &o->depends_list_node); + + // signal up + NCDModuleInst_Backend_Up(i); + } else { + // set not bound + o->is_bound = 0; + + // add to waiting depends list + LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + struct name *n = o->n; + + if (o->is_bound) { + ASSERT(n->cur_p) + + // remove from bound depends list + LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node); + + // continue resetting + if (n->cur_resetting) { + name_continue_resetting(n); + } + } else { + // remove from waiting depends list + LinkedList0_Remove(&n->waiting_depends_list, &o->depends_list_node); + + // free name if unused + name_free_if_unused(n); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + struct name *n = o->n; + ASSERT(!o->is_bound || n->cur_p) + + if (!(o->is_bound && n->cur_resetting)) { + return; + } + + // remove from bound depends list + LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node); + + // set not bound + o->is_bound = 0; + + // add to waiting depends list + LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node); + + // continue resetting + name_continue_resetting(n); +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + struct name *n = o->n; + ASSERT(!o->is_bound || n->cur_p) + + if (!o->is_bound) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(n->cur_p->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "dynamic_provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "dynamic_depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_dynamic_depend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/event_template.c b/app/src/main/jni/badvpn/ncd/modules/event_template.c new file mode 100644 index 0000000..2fcab9e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/event_template.c @@ -0,0 +1,184 @@ +/** + * @file event_template.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#define TemplateLog(o, ...) NCDModuleInst_Backend_Log((o)->i, (o)->blog_channel, __VA_ARGS__) + +static void enable_event (event_template *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->events_list)) + ASSERT(!o->enabled) + + // get event + struct event_template_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->events_list), struct event_template_event, events_list_node); + + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // grab enabled map + o->enabled_map = e->map; + + // append to free list + LinkedList1_Append(&o->free_list, &e->events_list_node); + + // set enabled + o->enabled = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +void event_template_new (event_template *o, NCDModuleInst *i, int blog_channel, int maxevents, void *user, + event_template_func_free func_free) +{ + ASSERT(maxevents > 0) + + // init arguments + o->i = i; + o->blog_channel = blog_channel; + o->user = user; + o->func_free = func_free; + + // allocate events array + if (!(o->events = BAllocArray(maxevents, sizeof(o->events[0])))) { + TemplateLog(o, BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init events lists + LinkedList1_Init(&o->events_list); + LinkedList1_Init(&o->free_list); + for (int i = 0; i < maxevents; i++) { + LinkedList1_Append(&o->free_list, &o->events[i].events_list_node); + } + + // set not enabled + o->enabled = 0; + + return; + +fail0: + o->func_free(o->user, 1); + return; +} + +void event_template_die (event_template *o) +{ + // free enabled map + if (o->enabled) { + BStringMap_Free(&o->enabled_map); + } + + // free event maps + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->events_list); + while (list_node) { + struct event_template_event *e = UPPER_OBJECT(list_node, struct event_template_event, events_list_node); + BStringMap_Free(&e->map); + list_node = LinkedList1Node_Next(list_node); + } + + // free events array + BFree(o->events); + + o->func_free(o->user, 0); + return; +} + +int event_template_getvar (event_template *o, const char *name, NCDValMem *mem, NCDValRef *out) +{ + ASSERT(o->enabled) + ASSERT(name) + + const char *val = BStringMap_Get(&o->enabled_map, name); + if (!val) { + return 0; + } + + *out = NCDVal_NewString(mem, val); + return 1; +} + +void event_template_queue (event_template *o, BStringMap map, int *out_was_empty) +{ + ASSERT(!LinkedList1_IsEmpty(&o->free_list)) + + // get event + struct event_template_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->free_list), struct event_template_event, events_list_node); + + // remove from free list + LinkedList1_Remove(&o->free_list, &e->events_list_node); + + // set map + e->map = map; + + // insert to events list + LinkedList1_Append(&o->events_list, &e->events_list_node); + + // enable if not already + if (!o->enabled) { + enable_event(o); + *out_was_empty = 1; + } else { + *out_was_empty = 0; + } +} + +void event_template_dequeue (event_template *o, int *out_is_empty) +{ + ASSERT(o->enabled) + + // free enabled map + BStringMap_Free(&o->enabled_map); + + // set not enabled + o->enabled = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + + // enable if there are more events + if (!LinkedList1_IsEmpty(&o->events_list)) { + enable_event(o); + *out_is_empty = 0; + } else { + *out_is_empty = 1; + } +} + +int event_template_is_enabled (event_template *o) +{ + return o->enabled; +} diff --git a/app/src/main/jni/badvpn/ncd/modules/event_template.h b/app/src/main/jni/badvpn/ncd/modules/event_template.h new file mode 100644 index 0000000..a3a5ea8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/event_template.h @@ -0,0 +1,64 @@ +/** + * @file event_template.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_MODULES_EVENT_TEMPLATE_H +#define BADVPN_NCD_MODULES_EVENT_TEMPLATE_H + +#include +#include +#include + +typedef void (*event_template_func_free) (void *user, int is_error); + +typedef struct { + NCDModuleInst *i; + int blog_channel; + void *user; + event_template_func_free func_free; + struct event_template_event *events; + LinkedList1 events_list; + LinkedList1 free_list; + int enabled; + BStringMap enabled_map; +} event_template; + +struct event_template_event { + BStringMap map; + LinkedList1Node events_list_node; +}; + +void event_template_new (event_template *o, NCDModuleInst *i, int blog_channel, int maxevents, void *user, + event_template_func_free func_free); +void event_template_die (event_template *o); +int event_template_getvar (event_template *o, const char *name, NCDValMem *mem, NCDValRef *out); +void event_template_queue (event_template *o, BStringMap map, int *out_was_empty); +void event_template_dequeue (event_template *o, int *out_is_empty); +int event_template_is_enabled (event_template *o); + +#endif diff --git a/app/src/main/jni/badvpn/ncd/modules/exit.c b/app/src/main/jni/badvpn/ncd/modules/exit.c new file mode 100644 index 0000000..d611e2e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/exit.c @@ -0,0 +1,91 @@ +/** + * @file exit.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * exit(string exit_code) + * + * Description: + * Initiates termination of the interpreter. Calling exit() multiple times will override + * the previously set exit code. When the interpreter receives a signal, it reacts + * equivalently to calling exit(1) (possibly overriding your error code). + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef exit_code_arg; + if (!NCDVal_ListRead(params->args, 1, &exit_code_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(exit_code_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse exit code + uintmax_t exit_code; + if (!ncd_read_uintmax(exit_code_arg, &exit_code) || exit_code >= INT_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong exit code value"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // initiate exit (before up!) + NCDModuleInst_Backend_InterpExit(i, exit_code); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "exit", + .func_new2 = func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_exit = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/explode.c b/app/src/main/jni/badvpn/ncd/modules/explode.c new file mode 100644 index 0000000..08425d5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/explode.c @@ -0,0 +1,232 @@ +/** + * @file explode.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * explode(string delimiter, string input [, string limit]) + * + * Description: + * Splits the string 'input' into a list of components. The first component + * is the part of 'input' until the first occurence of 'delimiter', if any. + * If 'delimiter' was found, the remaining components are defined recursively + * via the same procedure, starting with the part of 'input' after the first + * substring. + * 'delimiter' must be nonempty. + * + * Variables: + * list (empty) - the components of 'input', determined based on 'delimiter' + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + struct ExpArray arr; + size_t num; +}; + +struct substring { + char *data; + size_t len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef delimiter_arg; + NCDValRef input_arg; + NCDValRef limit_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &delimiter_arg, &input_arg) && !NCDVal_ListRead(params->args, 3, &delimiter_arg, &input_arg, &limit_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(delimiter_arg) || !NCDVal_IsString(input_arg) || (!NCDVal_IsInvalid(limit_arg) && !NCDVal_IsString(limit_arg))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t limit = SIZE_MAX; + if (!NCDVal_IsInvalid(limit_arg)) { + uintmax_t n; + if (!ncd_read_uintmax(limit_arg, &n) || n == 0) { + ModuleLog(i, BLOG_ERROR, "bad limit argument"); + goto fail0; + } + n--; + limit = (n <= SIZE_MAX ? n : SIZE_MAX); + } + + const char *del_data = NCDVal_StringData(delimiter_arg); + size_t del_len = NCDVal_StringLength(delimiter_arg); + + if (del_len == 0) { + ModuleLog(i, BLOG_ERROR, "delimiter must be nonempty"); + goto fail0; + } + + size_t *table = BAllocArray(del_len, sizeof(table[0])); + if (!table) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail0; + } + + build_substring_backtrack_table(del_data, del_len, table); + + if (!ExpArray_init(&o->arr, sizeof(struct substring), 8)) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail1; + } + o->num = 0; + + const char *data = NCDVal_StringData(input_arg); + size_t len = NCDVal_StringLength(input_arg); + + while (1) { + size_t start; + int is_end = 0; + if (limit == 0 || !find_substring(data, len, del_data, del_len, table, &start)) { + start = len; + is_end = 1; + } + + if (!ExpArray_resize(&o->arr, o->num + 1)) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail2; + } + + struct substring *elem = &((struct substring *)o->arr.v)[o->num]; + + if (!(elem->data = BAlloc(start))) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail2; + } + + memcpy(elem->data, data, start); + elem->len = start; + o->num++; + + if (is_end) { + break; + } + + data += start + del_len; + len -= start + del_len; + limit--; + } + + BFree(table); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail2: + while (o->num-- > 0) { + BFree(((struct substring *)o->arr.v)[o->num].data); + } + free(o->arr.v); +fail1: + BFree(table); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + while (o->num-- > 0) { + BFree(((struct substring *)o->arr.v)[o->num].data); + } + free(o->arr.v); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewList(mem, o->num); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + for (size_t j = 0; j < o->num; j++) { + struct substring *elem = &((struct substring *)o->arr.v)[j]; + NCDValRef str = NCDVal_NewStringBin(mem, (uint8_t *)elem->data, elem->len); + if (NCDVal_IsInvalid(str)) { + goto fail; + } + if (!NCDVal_ListAppend(*out, str)) { + goto fail; + } + } + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "explode", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_explode = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/file.c b/app/src/main/jni/badvpn/ncd/modules/file.c new file mode 100644 index 0000000..bda93ea --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/file.c @@ -0,0 +1,350 @@ +/** + * @file file.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * File I/O module. + * + * Synopsis: + * file_read(string filename) + * + * Variables: + * string (empty) - file contents + * + * Description: + * Reads the contents of a file. Reports an error if something goes wrong. + * WARNING: this uses fopen/fread/fclose, blocking the entire interpreter while + * the file is being read. For this reason, you should only use this + * to read small local files which will be read quickly, and especially + * not files on network mounts. + * + * Synopsis: + * file_write(string filename, string contents) + * + * Description: + * Writes a file, possibly overwriting an existing one. Reports an error if something + * goes wrong. + * WARNING: this is not an atomic operation; other programs may see the file in an + * inconsistent state while it is being written. Similarly, if writing + * fails, the file may remain in an inconsistent state indefinitely. + * If this is a problem, you should write the new contents to a temporary + * file and rename this temporary file to the live file. + * WARNING: this uses fopen/fwrite/fclose, blocking the entire interpreter while + * the file is being written. For this reason, you should only use this + * to write small local files which will be written quickly, and especially + * not files on network mounts. + * + * Synopsis: + * file_stat(string filename) + * file_lstat(string filename) + * + * Description: + * Retrieves information about a file. + * file_stat() follows symlinks; file_lstat() does not and allows retrieving information + * about a symlink. + * WARNING: this blocks the interpreter + * + * Variables: + * succeeded - whether the stat operation succeeded (true/false). If false, all other + * variables obtain the value "failed". + * type - file, dir, chr, blk, fifo, link, socket, other, failed + * size - size of the file, or failed + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct read_instance { + NCDModuleInst *i; + uint8_t *file_data; + size_t file_len; +}; + +struct stat_instance { + NCDModuleInst *i; + int succeeded; + struct stat result; +}; + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef filename_arg; + if (!NCDVal_ListRead(params->args, 1, &filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get null terminated name + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // read file + int res = read_file(filename_nts.data, &o->file_data, &o->file_len); + NCDValNullTermString_Free(&filename_nts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "failed to read file"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // free data + free(o->file_data); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewStringBin(mem, o->file_data, o->file_len); + return 1; + } + + return 0; +} + +static void write_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef filename_arg; + NCDValRef contents_arg; + if (!NCDVal_ListRead(params->args, 2, &filename_arg, &contents_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg) || !NCDVal_IsString(contents_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get null terminated name + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // write file + b_cstring contents_cstr = NCDVal_StringCstring(contents_arg); + int res = write_file_cstring(filename_nts.data, contents_cstr, 0, contents_cstr.length); + NCDValNullTermString_Free(&filename_nts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "failed to write file"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stat_func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_lstat) +{ + struct stat_instance *o = vo; + o->i = i; + + NCDValRef filename_arg; + if (!NCDVal_ListRead(params->args, 1, &filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = 0; + + if (!NCDVal_IsStringNoNulls(filename_arg)) { + goto out; + } + + // null terminate filename + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + int res; + if (is_lstat) { + res = lstat(filename_nts.data, &o->result); + } else { + res = stat(filename_nts.data, &o->result); + } + NCDValNullTermString_Free(&filename_nts); + + if (res < 0) { + goto out; + } + + o->succeeded = 1; + +out: + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stat_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + stat_func_new_common(vo, i, params, 0); +} + +static void lstat_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + stat_func_new_common(vo, i, params, 1); +} + +static int stat_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct stat_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (name == NCD_STRING_TYPE) { + const char *str; + + if (!o->succeeded) { + str = "failed"; + } else if (S_ISREG(o->result.st_mode)) { + str = "file"; + } else if (S_ISDIR(o->result.st_mode)) { + str = "dir"; + } else if (S_ISCHR(o->result.st_mode)) { + str = "chr"; + } else if (S_ISBLK(o->result.st_mode)) { + str = "blk"; + } else if (S_ISFIFO(o->result.st_mode)) { + str = "fifo"; + } else if (S_ISLNK(o->result.st_mode)) { + str = "link"; + } else if (S_ISSOCK(o->result.st_mode)) { + str = "socket"; + } else { + str = "other"; + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (name == NCD_STRING_SIZE) { + char str[50]; + if (!o->succeeded) { + strcpy(str, "failed"); + } else { + generate_decimal_repr_string((uintmax_t)o->result.st_size, str); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "file_read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar2, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_write", + .func_new2 = write_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_stat", + .func_new2 = stat_func_new, + .func_getvar2 = stat_func_getvar2, + .alloc_size = sizeof(struct stat_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_lstat", + .func_new2 = lstat_func_new, + .func_getvar2 = stat_func_getvar2, + .alloc_size = sizeof(struct stat_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_file = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/file_open.c b/app/src/main/jni/badvpn/ncd/modules/file_open.c new file mode 100644 index 0000000..f1b08fb --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/file_open.c @@ -0,0 +1,585 @@ +/** + * @file file_open.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * file_open(string filename, string mode [, map options]) + * + * Variables: + * string is_error - "true" if the file_open object is in error state, "false" + * otherwise + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Description: + * Opens a file for subsequent reading or writing. The 'mode' argument must + * be one of: "r", "w", "a", "r+", "w+", "a+"; it corresponds to the mode string + * that will be passed to the fopen() function. + * When the file_open() statement goes up, the error state is set depending on + * whether opening succeeded or failed. The 'is_error' variable should be used + * to check the error state. + * If an error occurs afterward within read(), write() or seek(), the error state + * is set, and the file_open() statement is toggled down and back up. This way, + * the same piece of user code can handle all file errors. + * + * Synopsis: + * file_open::read() + * + * Variables: + * string (empty) - the data which was read, or an empty string if EOF was reached + * string not_eof - "false" if EOF was reached, "true" if not + * + * Description: + * Reads data from an opened file. The file must not be in error state. + * If reading fails, this statement will never go up, the error state of the + * file_open() statement will be set, and the file_open() statement will trigger + * backtracking (go down and up). + * + * Synopsis: + * file_open::write(string data) + * + * Description: + * Writes data to an opened file. The file must not be in error state. + * If writing fails, this statement will never go up, the error state of the + * file_open() statement will be set, and the file_open() statement will trigger + * backtracking (go down and up). + * + * Synopsis: + * file_open::seek(string position, string whence) + * + * Description: + * Sets the file position indicator. The 'position' argument must be a possibly + * negative decimal number, and is interpreted relative to 'whence'. Here, 'whence' + * may be one of: + * - "set", meaning beginning of file, + * - "cur", meaning the current position, and + * - "end", meaning the end of file. + * Errors are handled as in read() and write(). Note that if the position argument + * is too small or too large to convert to off_t, this is not a seek error, and only + * the seek command will fail. + * + * Synopsis: + * file_open::close() + * + * Description: + * Closes the file. The file must not be in error state. + * Errors are handled as handled as in read() and write(), i.e. the process is + * backtracked to file_open() with the error state set. + * On success, the error state of the file is set (but without backtracking), and + * the close() statement goes up . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define READ_BUF_SIZE 8192 + +struct open_instance { + NCDModuleInst *i; + FILE *fh; + NCDBufStore store; +}; + +struct read_instance { + NCDModuleInst *i; + NCDBuf *buf; + size_t length; +}; + +static int parse_mode (b_cstring cstr, char *out) +{ + size_t pos = 0; + size_t left = cstr.length; + + if (left == 0) { + return 0; + } + switch (b_cstring_at(cstr, pos)) { + case 'r': + case 'w': + case 'a': + *out++ = b_cstring_at(cstr, pos); + pos++; + left--; + break; + default: + return 0; + } + + if (left == 0) { + goto finish; + } + switch (b_cstring_at(cstr, pos)) { + case '+': + *out++ = b_cstring_at(cstr, pos); + pos++; + left--; + break; + default: + return 0; + } + + if (left == 0) { + goto finish; + } + + return 0; + +finish: + *out = '\0'; + return 1; +} + +static void trigger_error (struct open_instance *o) +{ + if (o->fh) { + // close file + if (fclose(o->fh) != 0) { + ModuleLog(o->i, BLOG_ERROR, "fclose failed"); + } + + // set no file, indicating error + o->fh = NULL; + } + + // go down and up + NCDModuleInst_Backend_Down(o->i); + NCDModuleInst_Backend_Up(o->i); +} + +static void open_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct open_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef filename_arg; + NCDValRef mode_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &filename_arg, &mode_arg) && + !NCDVal_ListRead(params->args, 3, &filename_arg, &mode_arg, &options_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg) || !NCDVal_IsString(mode_arg) || + (!NCDVal_IsInvalid(options_arg) && !NCDVal_IsMap(options_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // check mode + char mode[5]; + if (!parse_mode(NCDVal_StringCstring(mode_arg), mode)) { + ModuleLog(o->i, BLOG_ERROR, "wrong mode"); + goto fail0; + } + + size_t read_size_opt = READ_BUF_SIZE; + + // parse options + if (!NCDVal_IsInvalid(options_arg)) { + int num_recognized = 0; + NCDValRef value; + + if (!NCDVal_IsInvalid(value = NCDVal_MapGetValue(options_arg, "read_size"))) { + uintmax_t read_size; + if (!NCDVal_IsString(value) || !ncd_read_uintmax(value, &read_size) || read_size > SIZE_MAX || read_size == 0) { + ModuleLog(o->i, BLOG_ERROR, "wrong read_size"); + goto fail0; + } + num_recognized++; + read_size_opt = read_size; + } + + if (NCDVal_MapCount(options_arg) > num_recognized) { + ModuleLog(o->i, BLOG_ERROR, "unrecognized options present"); + goto fail0; + } + } + + // init store + NCDBufStore_Init(&o->store, read_size_opt); + + // null terminate filename + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + + // open file + o->fh = fopen(filename_nts.data, mode); + NCDValNullTermString_Free(&filename_nts); + if (!o->fh) { + ModuleLog(o->i, BLOG_ERROR, "fopen failed"); + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDBufStore_Free(&o->store); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void open_func_die (void *vo) +{ + struct open_instance *o = vo; + + // close file + if (o->fh) { + if (fclose(o->fh) != 0) { + ModuleLog(o->i, BLOG_ERROR, "fclose failed"); + } + } + + // free store + NCDBufStore_Free(&o->store); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int open_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct open_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + *out = ncd_make_boolean(mem, !o->fh, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(o->i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&open_inst->store); + if (!o->buf) { + ModuleLog(o->i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // starting with empty buffer + char *data = NCDBuf_Data(o->buf); + size_t buf_size = NCDBufStore_BufSize(&open_inst->store); + o->length = 0; + + while (o->length < buf_size) { + // read + size_t readed = fread(data + o->length, 1, buf_size - o->length, open_inst->fh); + if (readed == 0) { + break; + } + ASSERT(readed <= buf_size - o->length) + + // increment length + o->length += readed; + } + + // if we couldn't read anything due to an error, trigger + // error in the open instance, and don't go up + if (o->length == 0 && !feof(open_inst->fh)) { + ModuleLog(o->i, BLOG_ERROR, "fread failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // release buffer + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->length, NCDBuf_RefTarget(o->buf)); + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + *out = ncd_make_boolean(mem, (o->length != 0), o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // write all the data + b_cstring data_cstr = NCDVal_StringCstring(data_arg); + B_CSTRING_LOOP(data_cstr, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + size_t written = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, open_inst->fh); + if (written == 0) { + ModuleLog(i, BLOG_ERROR, "fwrite failed"); + trigger_error(open_inst); + return; + } + ASSERT(written < chunk_length - chunk_pos) + chunk_pos += written; + } + }) + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void seek_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef position_arg; + NCDValRef whence_arg; + if (!NCDVal_ListRead(params->args, 2, &position_arg, &whence_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(position_arg) || !NCDVal_IsString(whence_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse position + int position_sign; + uintmax_t position_mag; + b_cstring position_cstr = NCDVal_StringCstring(position_arg); + if (!parse_signmag_integer_cstr(position_cstr, 0, position_cstr.length, &position_sign, &position_mag)) { + ModuleLog(i, BLOG_ERROR, "wrong position"); + goto fail0; + } + + // parse whence + int whence; + if (NCDVal_StringEquals(whence_arg, "set")) { + whence = SEEK_SET; + } + else if (NCDVal_StringEquals(whence_arg, "cur")) { + whence = SEEK_CUR; + } + else if (NCDVal_StringEquals(whence_arg, "end")) { + whence = SEEK_END; + } + else { + ModuleLog(i, BLOG_ERROR, "wrong whence"); + goto fail0; + } + + // determine min/max values of off_t (non-portable hack) + off_t off_t_min = (sizeof(off_t) == 8 ? INT64_MIN : INT32_MIN); + off_t off_t_max = (sizeof(off_t) == 8 ? INT64_MAX : INT32_MAX); + + // compute position as off_t + off_t position; + if (position_sign < 0 && position_mag > 0) { + if (position_mag - 1 > -(off_t_min + 1)) { + ModuleLog(i, BLOG_ERROR, "position underflow"); + goto fail0; + } + position = -(off_t)(position_mag - 1) - 1; + } else { + if (position_mag > off_t_max) { + ModuleLog(i, BLOG_ERROR, "position overflow"); + goto fail0; + } + position = position_mag; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // seek + if (fseeko(open_inst->fh, position, whence) < 0) { + ModuleLog(i, BLOG_ERROR, "fseeko failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void close_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // close + int res = fclose(open_inst->fh); + open_inst->fh = NULL; + if (res != 0) { + ModuleLog(i, BLOG_ERROR, "fclose failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "file_open", + .func_new2 = open_func_new, + .func_die = open_func_die, + .func_getvar2 = open_func_getvar, + .alloc_size = sizeof(struct open_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::write", + .func_new2 = write_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::seek", + .func_new2 = seek_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_file_open = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/foreach.c b/app/src/main/jni/badvpn/ncd/modules/foreach.c new file mode 100644 index 0000000..f0b3eff --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/foreach.c @@ -0,0 +1,715 @@ +/** + * @file foreach.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * foreach(list/map collection, string template, list args) + * + * Description: + * Initializes a template process for each element of list, sequentially, + * obeying to the usual execution model of NCD. + * It's equivalent to (except for special variables): + * + * call(template, args); + * ... + * call(template, args); # one call() for every element of list + * + * Template process specials: + * + * _index - (lists only) index of the list element corresponding to the template, + * process, as a decimal string, starting from zero + * _elem - (lists only) element of list corresponding to the template process + * _key - (maps only) key of the current map entry + * _val - (maps only) value of the current map entry + * _caller.X - X as seen from the foreach() statement + * + * Synopsis: + * foreach_emb(list/map collection, string template, string name1 [, string name2]) + * + * Description: + * Foreach for embedded templates; the desugaring process converts Foreach + * clauses into this statement. The called templates have direct access to + * objects as seen from this statement, and also some kind of access to the + * current element of the iteration, depending on the type of collection + * being iterated, and whether 'name2' is provided: + * List and one name: current element is named 'name1'. + * List and both names: current index is named 'name1', current element 'name2'. + * Map and one name: current key is named 'name1'. + * Map and both names: current key is named 'name1', current value 'name2'. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define ISTATE_WORKING 1 +#define ISTATE_UP 2 +#define ISTATE_WAITING 3 +#define ISTATE_TERMINATING 4 + +#define ESTATE_FORGOTTEN 1 +#define ESTATE_DOWN 2 +#define ESTATE_UP 3 +#define ESTATE_WAITING 4 +#define ESTATE_TERMINATING 5 + +struct element; + +struct instance { + NCDModuleInst *i; + NCDValRef template_name; + NCDValRef args; + NCD_string_id_t name1; + NCD_string_id_t name2; + BTimer timer; + struct element *elems; + int type; + int num_elems; + int gp; // good pointer + int ip; // initialized pointer + int state; +}; + +struct element { + struct instance *inst; + union { + struct { + NCDValRef list_elem; + }; + struct { + NCDValRef map_key; + NCDValRef map_val; + }; + }; + NCDModuleProcess process; + int i; + int state; +}; + +static void assert_state (struct instance *o); +static void work (struct instance *o); +static void advance (struct instance *o); +static void timer_handler (struct instance *o); +static void element_process_handler_event (NCDModuleProcess *process, int event); +static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static void instance_free (struct instance *o); + +enum {STRING_INDEX, STRING_ELEM, STRING_KEY, STRING_VAL}; + +static const char *strings[] = { + "_index", "_elem", "_key", "_val", NULL +}; + +static void assert_state (struct instance *o) +{ + ASSERT(o->num_elems >= 0) + ASSERT(o->gp >= 0) + ASSERT(o->ip >= 0) + ASSERT(o->gp <= o->num_elems) + ASSERT(o->ip <= o->num_elems) + ASSERT(o->gp <= o->ip) + +#ifndef NDEBUG + // check GP + for (int i = 0; i < o->gp; i++) { + if (i == o->gp - 1) { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING) + } else { + ASSERT(o->elems[i].state == ESTATE_UP) + } + } + + // check IP + int ip = o->num_elems; + while (ip > 0 && o->elems[ip - 1].state == ESTATE_FORGOTTEN) { + ip--; + } + ASSERT(o->ip == ip) + + // check gap + for (int i = o->gp; i < o->ip; i++) { + if (i == o->ip - 1) { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING || o->elems[i].state == ESTATE_TERMINATING) + } else { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING) + } + } +#endif +} + +static void work (struct instance *o) +{ + assert_state(o); + + // stop timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + if (o->state == ISTATE_WAITING) { + return; + } + + if (o->state == ISTATE_UP && !(o->gp == o->ip && o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP))) { + // signal down + NCDModuleInst_Backend_Down(o->i); + + // set state waiting + o->state = ISTATE_WAITING; + return; + } + + if (o->gp < o->ip) { + // get last element + struct element *le = &o->elems[o->ip - 1]; + ASSERT(le->state != ESTATE_FORGOTTEN) + + // start terminating if not already + if (le->state != ESTATE_TERMINATING) { + // request termination + NCDModuleProcess_Terminate(&le->process); + + // set element state terminating + le->state = ESTATE_TERMINATING; + } + + return; + } + + if (o->state == ISTATE_TERMINATING) { + // finally die + instance_free(o); + return; + } + + if (o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)) { + if (o->state == ISTATE_WORKING) { + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = ISTATE_UP; + } + + return; + } + + if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_WAITING) { + // get last element + struct element *le = &o->elems[o->gp - 1]; + + // continue process + NCDModuleProcess_Continue(&le->process); + + // set state down + le->state = ESTATE_DOWN; + return; + } + + if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_DOWN) { + return; + } + + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + + advance(o); + return; +} + +static void advance (struct instance *o) +{ + assert_state(o); + ASSERT(o->gp == o->ip) + ASSERT(o->gp < o->num_elems) + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN) + + // get next element + struct element *e = &o->elems[o->gp]; + + // init process + if (!NCDModuleProcess_InitValue(&e->process, o->i, o->template_name, o->args, element_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&e->process, element_process_func_getspecialobj); + + // set element state down + e->state = ESTATE_DOWN; + + // increment GP and IP + o->gp++; + o->ip++; + return; + +fail: + // set timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +static void timer_handler (struct instance *o) +{ + assert_state(o); + ASSERT(o->gp == o->ip) + ASSERT(o->gp < o->num_elems) + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN) + + advance(o); + return; +} + +static void element_process_handler_event (NCDModuleProcess *process, int event) +{ + struct element *e = UPPER_OBJECT(process, struct element, process); + struct instance *o = e->inst; + assert_state(o); + ASSERT(e->i < o->ip) + ASSERT(e->state != ESTATE_FORGOTTEN) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(e->state == ESTATE_DOWN) + ASSERT(o->gp == o->ip) + ASSERT(o->gp == e->i + 1) + + // set element state up + e->state = ESTATE_UP; + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(e->state == ESTATE_UP) + + // set element state waiting + e->state = ESTATE_WAITING; + + // bump down GP + if (o->gp > e->i + 1) { + o->gp = e->i + 1; + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(e->state == ESTATE_TERMINATING) + ASSERT(o->gp < o->ip) + ASSERT(o->ip == e->i + 1) + + // free process + NCDModuleProcess_Free(&e->process); + + // set element state forgotten + e->state = ESTATE_FORGOTTEN; + + // decrement IP + o->ip--; + } break; + + default: ASSERT(0); + } + + work(o); + return; +} + +static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct element *e = UPPER_OBJECT(process, struct element, process); + struct instance *o = e->inst; + ASSERT(e->state != ESTATE_FORGOTTEN) + + switch (o->type) { + case NCDVAL_LIST: { + NCD_string_id_t index_name = (o->name2 >= 0 ? o->name1 : -1); + NCD_string_id_t elem_name = (o->name2 >= 0 ? o->name2 : o->name1); + + if (index_name >= 0 && name == index_name) { + *out_object = NCDObject_Build(-1, e, element_list_index_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (name == elem_name) { + *out_object = NCDObject_Build(-1, e, element_list_elem_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } break; + case NCDVAL_MAP: { + NCD_string_id_t key_name = o->name1; + NCD_string_id_t val_name = o->name2; + + if (name == key_name) { + *out_object = NCDObject_Build(-1, e, element_map_key_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (val_name >= 0 && name == val_name) { + *out_object = NCDObject_Build(-1, e, element_map_val_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } break; + } + + if (NCDVal_IsInvalid(o->args)) { + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); + } + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, e, NCDObject_no_getvar, element_caller_object_func_getobj); + return 1; + } + + return 0; +} + +static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(!NCDVal_IsInvalid(o->args)) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_LIST) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = ncd_make_uintmax(mem, e->i); + return 1; +} + +static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_LIST) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->list_elem); + return 1; +} + +static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_MAP) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->map_key); + return 1; +} + +static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_MAP) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->map_val); + return 1; +} + +static void func_new_common (void *vo, NCDModuleInst *i, NCDValRef collection, NCDValRef template_name, NCDValRef args, NCD_string_id_t name1, NCD_string_id_t name2) +{ + ASSERT(!NCDVal_IsInvalid(collection)) + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(name1 >= 0) + + struct instance *o = vo; + o->i = i; + + o->type = NCDVal_Type(collection); + o->template_name = template_name; + o->args = args; + o->name1 = name1; + o->name2 = name2; + + // init timer + btime_t retry_time = NCDModuleInst_Backend_InterpGetRetryTime(i); + BTimer_Init(&o->timer, retry_time, (BTimer_handler)timer_handler, o); + + size_t num_elems; + NCDValMapElem cur_map_elem; + + switch (o->type) { + case NCDVAL_LIST: { + num_elems = NCDVal_ListCount(collection); + } break; + case NCDVAL_MAP: { + num_elems = NCDVal_MapCount(collection); + cur_map_elem = NCDVal_MapOrderedFirst(collection); + } break; + default: + ModuleLog(i, BLOG_ERROR, "invalid collection type"); + goto fail0; + } + + if (num_elems > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "too many elements"); + goto fail0; + } + o->num_elems = num_elems; + + // allocate elements + if (!(o->elems = BAllocArray(o->num_elems, sizeof(o->elems[0])))) { + ModuleLog(i, BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + for (int j = 0; j < o->num_elems; j++) { + struct element *e = &o->elems[j]; + + // set instance + e->inst = o; + + // set index + e->i = j; + + // set state forgotten + e->state = ESTATE_FORGOTTEN; + + // set values + switch (o->type) { + case NCDVAL_LIST: { + e->list_elem = NCDVal_ListGet(collection, j); + } break; + case NCDVAL_MAP: { + e->map_key = NCDVal_MapElemKey(collection, cur_map_elem); + e->map_val = NCDVal_MapElemVal(collection, cur_map_elem); + cur_map_elem = NCDVal_MapOrderedNext(collection, cur_map_elem); + } break; + } + } + + // set GP and IP zero + o->gp = 0; + o->ip = 0; + + // set state working + o->state = ISTATE_WORKING; + + work(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_foreach (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_collection; + NCDValRef arg_template; + NCDValRef arg_args; + if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template) || !NCDVal_IsList(arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCD_string_id_t name1; + NCD_string_id_t name2; + + switch (NCDVal_Type(arg_collection)) { + case NCDVAL_LIST: { + name1 = ModuleString(i, STRING_INDEX); + name2 = ModuleString(i, STRING_ELEM); + } break; + case NCDVAL_MAP: { + name1 = ModuleString(i, STRING_KEY); + name2 = ModuleString(i, STRING_VAL); + } break; + default: + ModuleLog(i, BLOG_ERROR, "invalid collection type"); + goto fail0; + } + + func_new_common(vo, i, arg_collection, arg_template, arg_args, name1, name2); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_foreach_emb (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_collection; + NCDValRef arg_template; + NCDValRef arg_name1; + NCDValRef arg_name2 = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_name1) && !NCDVal_ListRead(params->args, 4, &arg_collection, &arg_template, &arg_name1, &arg_name2)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template) || !NCDVal_IsString(arg_name1) || (!NCDVal_IsInvalid(arg_name2) && !NCDVal_IsString(arg_name2))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCD_string_id_t name1 = ncd_get_string_id(arg_name1, i->params->iparams->string_index); + if (name1 < 0) { + ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail0; + } + + NCD_string_id_t name2 = -1; + if (!NCDVal_IsInvalid(arg_name2)) { + name2 = ncd_get_string_id(arg_name2, i->params->iparams->string_index); + if (name2 < 0) { + ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail0; + } + } + + func_new_common(vo, i, arg_collection, arg_template, NCDVal_NewInvalid(), name1, name2); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(o->gp == 0) + ASSERT(o->ip == 0) + + // free elements + BFree(o->elems); + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + assert_state(o); + ASSERT(o->state != ISTATE_TERMINATING) + + // set GP zero + o->gp = 0; + + // set state terminating + o->state = ISTATE_TERMINATING; + + work(o); + return; +} + +static void func_clean (void *vo) +{ + struct instance *o = vo; + + if (o->state != ISTATE_WAITING) { + return; + } + + // set state working + o->state = ISTATE_WORKING; + + work(o); + return; +} + +static struct NCDModule modules[] = { + { + .type = "foreach", + .func_new2 = func_new_foreach, + .func_die = func_die, + .func_clean = func_clean, + .alloc_size = sizeof(struct instance) + }, { + .type = "foreach_emb", + .func_new2 = func_new_foreach_emb, + .func_die = func_die, + .func_clean = func_clean, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_foreach = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/from_string.c b/app/src/main/jni/badvpn/ncd/modules/from_string.c new file mode 100644 index 0000000..3bc446b --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/from_string.c @@ -0,0 +1,125 @@ +/** + * @file from_string.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * from_string(string str) + * Variables: + * (empty) - str, parsed as a value + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef val; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // parse value string + if (!NCDValParser_Parse(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->mem, &o->val)) { + ModuleLog(i, BLOG_ERROR, "failed to parse"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->val); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "from_string", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_from_string = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/getargs.c b/app/src/main/jni/badvpn/ncd/modules/getargs.c new file mode 100644 index 0000000..f620a65 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/getargs.c @@ -0,0 +1,98 @@ +/** + * @file getargs.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * getargs() + * + * Variables: + * (empty) - list of extra command line arguments that were passed to the intrepreter + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + if (!NCDModuleInst_Backend_InterpGetArgs(o->i, mem, out)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleInst_Backend_InterpGetArgs failed"); + return 0; + } + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "getargs", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_getargs = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/getenv.c b/app/src/main/jni/badvpn/ncd/modules/getenv.c new file mode 100644 index 0000000..2cb3e91 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/getenv.c @@ -0,0 +1,153 @@ +/** + * @file getenv.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * getenv(string name) + * + * Variables: + * string (empty) - if the environment value exists, its value + * string exists - "true" if the variable exists, "false" if not + */ + +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + char *value; +}; + +enum {STRING_EXISTS}; + +static const char *strings[] = {"exists", NULL}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->value = NULL; + + if (NCDVal_StringHasNulls(name_arg)) { + goto out; + } + + NCDValNullTermString nts; + if (!NCDVal_StringNullTerminate(name_arg, &nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + char *result = getenv(nts.data); + NCDValNullTermString_Free(&nts); + if (!result) { + goto out; + } + + o->value = b_strdup(result); + if (!o->value) { + ModuleLog(i, BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + +out: + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free value + if (o->value) { + BFree(o->value); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY && o->value) { + *out = NCDVal_NewString(mem, o->value); + return 1; + } + + if (name == ModuleString(o->i, STRING_EXISTS)) { + *out = ncd_make_boolean(mem, !!o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "getenv", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_getenv = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/if.c b/app/src/main/jni/badvpn/ncd/modules/if.c new file mode 100644 index 0000000..3bbd362 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/if.c @@ -0,0 +1,103 @@ +/** + * @file if.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Conditional module. + * + * Synopsis: if(string cond) + * Description: on initialization, transitions to UP state if cond equals "true", else + * remains in the DOWN state indefinitely. + * + * Synopsis: ifnot(string cond) + * Description: on initialization, transitions to UP state if cond does not equal "true", else + * remains in the DOWN state indefinitely. + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void new_templ (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_not) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // compute logical value of argument + int c = ncd_read_boolean(arg); + + // signal up if needed + if ((is_not && !c) || (!is_not && c)) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(i, params, 0); +} + +static void func_new_not (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "if", + .func_new2 = func_new + }, { + .type = "ifnot", + .func_new2 = func_new_not + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_if = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/imperative.c b/app/src/main/jni/badvpn/ncd/modules/imperative.c new file mode 100644 index 0000000..f964888 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/imperative.c @@ -0,0 +1,324 @@ +/** + * @file imperative.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Imperative statement. + * + * Synopsis: + * imperative(string init_template, list init_args, string deinit_template, list deinit_args, string deinit_timeout) + * + * Description: + * Does the following, in order: + * 1. Starts a template process from (init_template, init_args) and waits for it to + * initialize completely. + * 2. Initiates termination of the process and wait for it to terminate. + * 3. Puts the statement UP, then waits for a statement termination request (which may + * already have been received). + * 4. Starts a template process from (deinit_template, deinit_args) and waits for it + * to initialize completely, or for the timeout to elapse. + * 5. Initiates termination of the process and wait for it to terminate. + * 6. Terminates the statement. + * + * If init_template="", steps (1-2) are skipped. + * If deinit_template="", steps (4-5) are skipped. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_INIT_WORKING 1 +#define STATE_INIT_CLEANING 2 +#define STATE_UP 3 +#define STATE_DEINIT_WORKING 4 +#define STATE_DEINIT_CLEANING 5 + +struct instance { + NCDModuleInst *i; + NCDValRef deinit_template; + NCDValRef deinit_args; + BTimer deinit_timer; + NCDModuleProcess process; + int state; + int dying; +}; + +static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler); +static void go_deinit (struct instance *o); +static void init_process_handler_event (NCDModuleProcess *process, int event); +static void deinit_process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void deinit_timer_handler (struct instance *o); +static void instance_free (struct instance *o); + +static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler) +{ + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsList(args)) + + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + return 0; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + return 1; +} + +static void go_deinit (struct instance *o) +{ + ASSERT(o->dying) + + // deinit is no-op? + if (ncd_is_none(o->deinit_template)) { + instance_free(o); + return; + } + + // start deinit process + if (!start_process(o, o->deinit_template, o->deinit_args, deinit_process_handler_event)) { + instance_free(o); + return; + } + + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->deinit_timer); + + // set state deinit working + o->state = STATE_DEINIT_WORKING; +} + +static void init_process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_INIT_WORKING) + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state init cleaning + o->state = STATE_INIT_CLEANING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_INIT_CLEANING) + + // free process + NCDModuleProcess_Free(&o->process); + + // were we requested to die aleady? + if (o->dying) { + go_deinit(o); + return; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = STATE_UP; + } break; + + default: ASSERT(0); + } +} + +static void deinit_process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->dying) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_DEINIT_WORKING) + + // stop timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->deinit_timer); + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state deinit cleaning + o->state = STATE_DEINIT_CLEANING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_DEINIT_CLEANING) + + // free process + NCDModuleProcess_Free(&o->process); + + // die + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->state != STATE_UP) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj); + return 1; + } + + return 0; +} + +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + ASSERT(o->state != STATE_UP) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void deinit_timer_handler (struct instance *o) +{ + ASSERT(o->state == STATE_DEINIT_WORKING) + + ModuleLog(o->i, BLOG_ERROR, "imperative deinit timeout elapsed"); + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state deinit cleaning + o->state = STATE_DEINIT_CLEANING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef init_template_arg; + NCDValRef init_args; + NCDValRef deinit_template_arg; + NCDValRef deinit_timeout_arg; + if (!NCDVal_ListRead(params->args, 5, &init_template_arg, &init_args, &deinit_template_arg, &o->deinit_args, &deinit_timeout_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(init_template_arg) || !NCDVal_IsList(init_args) || + !NCDVal_IsString(deinit_template_arg) || !NCDVal_IsList(o->deinit_args) || + !NCDVal_IsString(deinit_timeout_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->deinit_template = deinit_template_arg; + + // read timeout + uintmax_t timeout; + if (!ncd_read_uintmax(deinit_timeout_arg, &timeout) || timeout > UINT64_MAX){ + ModuleLog(i, BLOG_ERROR, "wrong timeout"); + goto fail0; + } + + // init timer + BTimer_Init(&o->deinit_timer, timeout, (BTimer_handler)deinit_timer_handler, o); + + if (ncd_is_none(init_template_arg)) { + // signal up + NCDModuleInst_Backend_Up(i); + + // set state up + o->state = STATE_UP; + } else { + // start init process + if (!start_process(o, init_template_arg, init_args, init_process_handler_event)) { + goto fail0; + } + + // set state init working + o->state = STATE_INIT_WORKING; + } + + // set not dying + o->dying = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // set dying + o->dying = 1; + + if (o->state == STATE_UP) { + go_deinit(o); + return; + } +} + +static struct NCDModule modules[] = { + { + .type = "imperative", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_imperative = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/implode.c b/app/src/main/jni/badvpn/ncd/modules/implode.c new file mode 100644 index 0000000..0520eca --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/implode.c @@ -0,0 +1,155 @@ +/** + * @file implode.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * implode(string glue, list(string) pieces) + * + * Variables: + * string (empty) - concatenation of strings in 'pieces', with 'glue' in between + * every two elements. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + char *result; + size_t result_len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef glue_arg; + NCDValRef pieces_arg; + if (!NCDVal_ListRead(params->args, 2, &glue_arg, &pieces_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(glue_arg) || !NCDVal_IsList(pieces_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init result string + ExpString str; + if (!ExpString_Init(&str)) { + ModuleLog(i, BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + size_t count = NCDVal_ListCount(pieces_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef piece = NCDVal_ListGet(pieces_arg, j); + + // check piece type + if (!NCDVal_IsString(piece)) { + ModuleLog(i, BLOG_ERROR, "wrong piece type"); + goto fail1; + } + + // append glue + if (j > 0) { + if (!ExpString_AppendBinary(&str, (const uint8_t *)NCDVal_StringData(glue_arg), NCDVal_StringLength(glue_arg))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + } + + // append piece + if (!ExpString_AppendBinary(&str, (const uint8_t *)NCDVal_StringData(piece), NCDVal_StringLength(piece))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + } + + // store result + o->result = ExpString_Get(&str); + o->result_len = ExpString_Length(&str); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + ExpString_Free(&str); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free result + free(o->result); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->result, o->result_len); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "implode", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_implode = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/index.c b/app/src/main/jni/badvpn/ncd/modules/index.c new file mode 100644 index 0000000..fe68bdd --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/index.c @@ -0,0 +1,164 @@ +/** + * @file index.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * index index(string value) + * index index::next() + * + * Description: + * Non-negative integer with range of a size_t. + * The first form creates an index from the given decimal string. + * The second form cretes an index with value one more than an existing + * index. + * + * Variables: + * string (empty) - the index value. Note this may be different from + * than the value given to index() if it was not in normal form. + */ + +#include +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + size_t value; +}; + +static void func_new_templ (void *vo, NCDModuleInst *i, size_t value) +{ + struct instance *o = vo; + o->i = i; + + // set value + o->value = value; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void func_new_from_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_value; + if (!NCDVal_ListRead(params->args, 1, &arg_value)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_value)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse value + uintmax_t value; + if (!ncd_read_uintmax(arg_value, &value)) { + ModuleLog(i, BLOG_ERROR, "wrong value"); + goto fail0; + } + + // check overflow + if (value > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "value too large"); + goto fail0; + } + + func_new_templ(vo, i, value); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_from_index (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *index = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check overflow + if (index->value == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "overflow"); + goto fail0; + } + + func_new_templ(vo, i, index->value + 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_uintmax(mem, o->value); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "index", + .func_new2 = func_new_from_value, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "index::next", + .base_type = "index", + .func_new2 = func_new_from_index, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_index = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/list.c b/app/src/main/jni/badvpn/ncd/modules/list.c new file mode 100644 index 0000000..e1df1f1 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/list.c @@ -0,0 +1,871 @@ +/** + * @file list.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * List construction module. + * + * Synopsis: + * list(elem1, ..., elemN) + * list listfrom(list l1, ..., list lN) + * + * Description: + * The first form creates a list with the given elements. + * The second form creates a list by concatenating the given + * lists. + * + * Variables: + * (empty) - list containing elem1, ..., elemN + * length - number of elements in list + * + * Synopsis: list::append(arg) + * + * Synopsis: list::appendv(list arg) + * Description: Appends the elements of arg to the list. + * + * Synopsis: list::length() + * Variables: + * (empty) - number of elements in list at the time of initialization + * of this method + * + * Synopsis: list::get(string index) + * Variables: + * (empty) - element of list at position index (starting from zero) at the time of initialization + * + * Synopsis: list::shift() + * + * Synopsis: list::contains(value) + * Variables: + * (empty) - "true" if list contains value, "false" if not + * + * Synopsis: + * list::find(start_pos, value) + * Description: + * finds the first occurrence of 'value' in the list at position >='start_pos'. + * Variables: + * pos - position of element, or "none" if not found + * found - "true" if found, "false" if not + * + * Sysnopsis: + * list::remove_at(remove_pos) + * Description: + * Removes the element at position 'remove_pos', which must refer to an existing element. + * + * Synopsis: + * list::remove(value) + * Description: + * Removes the first occurrence of value in the list, which must be in the list. + * + * Synopsis: + * list::set(list l1, ..., list lN) + * Description: + * Replaces the list with the concatenation of given lists. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct elem { + IndexedListNode il_node; + NCDValMem mem; + NCDValRef val; +}; + +struct instance { + NCDModuleInst *i; + IndexedList il; +}; + +struct length_instance { + NCDModuleInst *i; + uint64_t length; +}; + +struct get_instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef val; +}; + +struct contains_instance { + NCDModuleInst *i; + int contains; +}; + +struct find_instance { + NCDModuleInst *i; + int is_found; + uint64_t found_pos; +}; + +static uint64_t list_count (struct instance *o) +{ + return IndexedList_Count(&o->il); +} + +static struct elem * insert_value (NCDModuleInst *i, struct instance *o, NCDValRef val, uint64_t idx) +{ + ASSERT(idx <= list_count(o)) + ASSERT(!NCDVal_IsInvalid(val)) + + struct elem *e = malloc(sizeof(*e)); + if (!e) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + NCDValMem_Init(&e->mem); + + e->val = NCDVal_NewCopy(&e->mem, val); + if (NCDVal_IsInvalid(e->val)) { + goto fail1; + } + + IndexedList_InsertAt(&o->il, &e->il_node, idx); + + return e; + +fail1: + NCDValMem_Free(&e->mem); + free(e); +fail0: + return NULL; +} + +static void remove_elem (struct instance *o, struct elem *e) +{ + IndexedList_Remove(&o->il, &e->il_node); + NCDValMem_Free(&e->mem); + free(e); +} + +static struct elem * get_elem_at (struct instance *o, uint64_t idx) +{ + ASSERT(idx < list_count(o)) + + IndexedListNode *iln = IndexedList_GetAt(&o->il, idx); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static struct elem * get_first_elem (struct instance *o) +{ + ASSERT(list_count(o) > 0) + + IndexedListNode *iln = IndexedList_GetFirst(&o->il); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static struct elem * get_last_elem (struct instance *o) +{ + ASSERT(list_count(o) > 0) + + IndexedListNode *iln = IndexedList_GetLast(&o->il); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static void cut_list_front (struct instance *o, uint64_t count) +{ + while (list_count(o) > count) { + remove_elem(o, get_first_elem(o)); + } +} + +static void cut_list_back (struct instance *o, uint64_t count) +{ + while (list_count(o) > count) { + remove_elem(o, get_last_elem(o)); + } +} + +static int append_list_contents (NCDModuleInst *i, struct instance *o, NCDValRef args) +{ + ASSERT(NCDVal_IsList(args)) + + uint64_t orig_count = list_count(o); + + size_t append_count = NCDVal_ListCount(args); + + for (size_t j = 0; j < append_count; j++) { + NCDValRef elem = NCDVal_ListGet(args, j); + if (!insert_value(i, o, elem, list_count(o))) { + goto fail; + } + } + + return 1; + +fail: + cut_list_back(o, orig_count); + return 0; +} + +static int append_list_contents_contents (NCDModuleInst *i, struct instance *o, NCDValRef args) +{ + ASSERT(NCDVal_IsList(args)) + + uint64_t orig_count = list_count(o); + + size_t append_count = NCDVal_ListCount(args); + + for (size_t j = 0; j < append_count; j++) { + NCDValRef elem = NCDVal_ListGet(args, j); + + if (!NCDVal_IsList(elem)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail; + } + + if (!append_list_contents(i, o, elem)) { + goto fail; + } + } + + return 1; + +fail: + cut_list_back(o, orig_count); + return 0; +} + +static struct elem * find_elem (struct instance *o, NCDValRef val, uint64_t start_idx, uint64_t *out_idx) +{ + if (start_idx >= list_count(o)) { + return NULL; + } + + for (IndexedListNode *iln = IndexedList_GetAt(&o->il, start_idx); iln; iln = IndexedList_GetNext(&o->il, iln)) { + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + if (NCDVal_Compare(e->val, val) == 0) { + if (out_idx) { + *out_idx = start_idx; + } + return e; + } + start_idx++; + } + + return NULL; +} + +static int list_to_value (NCDModuleInst *i, struct instance *o, NCDValMem *mem, NCDValRef *out_val) +{ + *out_val = NCDVal_NewList(mem, IndexedList_Count(&o->il)); + if (NCDVal_IsInvalid(*out_val)) { + goto fail; + } + + for (IndexedListNode *iln = IndexedList_GetFirst(&o->il); iln; iln = IndexedList_GetNext(&o->il, iln)) { + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + NCDValRef copy = NCDVal_NewCopy(mem, e->val); + if (NCDVal_IsInvalid(copy)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out_val, copy)) { + goto fail; + } + } + + return 1; + +fail: + return 0; +} + +static void func_new_list (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init list + IndexedList_Init(&o->il); + + // append contents + if (!append_list_contents(i, o, params->args)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + cut_list_front(o, 0); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_listfrom (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init list + IndexedList_Init(&o->il); + + // append contents contents + if (!append_list_contents_contents(i, o, params->args)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + cut_list_front(o, 0); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free list elements + cut_list_front(o, 0); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + if (!list_to_value(o->i, o, mem, out)) { + return 0; + } + + return 1; + } + + if (!strcmp(name, "length")) { + *out = ncd_make_uintmax(mem, list_count(o)); + return 1; + } + + return 0; +} + +static void append_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // append + if (!insert_value(i, mo, arg, list_count(mo))) { + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void appendv_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // append + if (!append_list_contents(i, mo, arg)) { + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void length_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct length_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // remember length + o->length = list_count(mo); + + // signal up + NCDModuleInst_Backend_Up(o->i); + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void length_func_die (void *vo) +{ + struct length_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int length_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct length_instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_uintmax(mem, o->length); + return 1; + } + + return 0; +} + +static void get_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct get_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef index_arg; + if (!NCDVal_ListRead(params->args, 1, &index_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(index_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + uintmax_t index; + if (!ncd_read_uintmax(index_arg, &index)) { + ModuleLog(o->i, BLOG_ERROR, "wrong value"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check index + if (index >= list_count(mo)) { + ModuleLog(o->i, BLOG_ERROR, "no element at index %"PRIuMAX, index); + goto fail0; + } + + // get element + struct elem *e = get_elem_at(mo, index); + + // init mem + NCDValMem_Init(&o->mem); + + // copy value + o->val = NCDVal_NewCopy(&o->mem, e->val); + if (NCDVal_IsInvalid(o->val)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void get_func_die (void *vo) +{ + struct get_instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int get_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct get_instance *o = vo; + + if (!strcmp(name, "")) { + *out = NCDVal_NewCopy(mem, o->val); + return 1; + } + + return 0; +} + +static void shift_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check first + if (list_count(mo) == 0) { + ModuleLog(i, BLOG_ERROR, "list has no elements"); + goto fail0; + } + + // remove first + remove_elem(mo, get_first_elem(mo)); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void contains_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct contains_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // search + o->contains = !!find_elem(mo, value_arg, 0, NULL); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void contains_func_die (void *vo) +{ + struct contains_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int contains_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct contains_instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->contains, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void find_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct find_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef start_pos_arg; + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 2, &start_pos_arg, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(start_pos_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read start position + uintmax_t start_pos; + if (!ncd_read_uintmax(start_pos_arg, &start_pos) || start_pos > UINT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong start pos"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // find + o->is_found = !!find_elem(mo, value_arg, start_pos, &o->found_pos); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void find_func_die (void *vo) +{ + struct find_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int find_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct find_instance *o = vo; + + if (!strcmp(name, "pos")) { + char value[64] = "none"; + + if (o->is_found) { + generate_decimal_repr_string(o->found_pos, value); + } + + *out = NCDVal_NewString(mem, value); + return 1; + } + + if (!strcmp(name, "found")) { + *out = ncd_make_boolean(mem, o->is_found, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void removeat_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef remove_pos_arg; + if (!NCDVal_ListRead(params->args, 1, &remove_pos_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(remove_pos_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read position + uintmax_t remove_pos; + if (!ncd_read_uintmax(remove_pos_arg, &remove_pos)) { + ModuleLog(i, BLOG_ERROR, "wrong pos"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check position + if (remove_pos >= list_count(mo)) { + ModuleLog(i, BLOG_ERROR, "pos out of range"); + goto fail0; + } + + // remove + remove_elem(mo, get_elem_at(mo, remove_pos)); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void remove_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // find element + struct elem *e = find_elem(mo, value_arg, 0, NULL); + if (!e) { + ModuleLog(i, BLOG_ERROR, "value does not exist"); + goto fail0; + } + + // remove element + remove_elem(mo, e); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void set_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // remember old count + uint64_t old_count = list_count(mo); + + // append contents of our lists + if (!append_list_contents_contents(i, mo, params->args)) { + goto fail0; + } + + // remove old elements + cut_list_front(mo, list_count(mo) - old_count); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "list", + .func_new2 = func_new_list, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "listfrom", + .base_type = "list", + .func_new2 = func_new_listfrom, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "concatlist", // alias for listfrom + .base_type = "list", + .func_new2 = func_new_listfrom, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "list::append", + .func_new2 = append_func_new + }, { + .type = "list::appendv", + .func_new2 = appendv_func_new + }, { + .type = "list::length", + .func_new2 = length_func_new, + .func_die = length_func_die, + .func_getvar = length_func_getvar, + .alloc_size = sizeof(struct length_instance) + }, { + .type = "list::get", + .func_new2 = get_func_new, + .func_die = get_func_die, + .func_getvar = get_func_getvar, + .alloc_size = sizeof(struct get_instance) + }, { + .type = "list::shift", + .func_new2 = shift_func_new + }, { + .type = "list::contains", + .func_new2 = contains_func_new, + .func_die = contains_func_die, + .func_getvar = contains_func_getvar, + .alloc_size = sizeof(struct contains_instance) + }, { + .type = "list::find", + .func_new2 = find_func_new, + .func_die = find_func_die, + .func_getvar = find_func_getvar, + .alloc_size = sizeof(struct find_instance) + }, { + .type = "list::remove_at", + .func_new2 = removeat_func_new + }, { + .type = "list::remove", + .func_new2 = remove_func_new + }, { + .type = "list::set", + .func_new2 = set_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_list = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/load_module.c b/app/src/main/jni/badvpn/ncd/modules/load_module.c new file mode 100644 index 0000000..f9bf832 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/load_module.c @@ -0,0 +1,313 @@ +/** + * @file load_module.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * load_module(string name) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct global { + LinkedList0 modules_list; +}; + +struct module { + char *name; + void *lib_handle; + int ncdmodule_loaded; + LinkedList0Node modules_list_node; +}; + +static struct module * find_module (const char *name, struct global *g) +{ + for (LinkedList0Node *ln = LinkedList0_GetFirst(&g->modules_list); ln; ln = LinkedList0Node_Next(ln)) { + struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node); + if (!strcmp(mod->name, name)) { + return mod; + } + } + return NULL; +} + +static struct module * module_init (const char *name, NCDModuleInst *i) +{ + struct global *g = ModuleGlobal(i); + ASSERT(!find_module(name, g)) + + struct module *mod = BAlloc(sizeof(*mod)); + if (!mod) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + mod->name = b_strdup(name); + if (!mod->name) { + ModuleLog(i, BLOG_ERROR, "b_strdup failed"); + goto fail1; + } + + mod->lib_handle = NULL; + mod->ncdmodule_loaded = 0; + LinkedList0_Prepend(&g->modules_list, &mod->modules_list_node); + + return mod; + +fail1: + BFree(mod); +fail0: + return NULL; +} + +static void module_free (struct module *mod, struct global *g) +{ + LinkedList0_Remove(&g->modules_list, &mod->modules_list_node); + if (mod->lib_handle) { + if (dlclose(mod->lib_handle) != 0) { + BLog(BLOG_ERROR, "dlclose failed"); + } + } + BFree(mod->name); + BFree(mod); +} + +static char * x_read_link (const char *path) +{ + size_t size = 32; + char *buf = BAlloc(size + 1); + if (!buf) { + goto fail0; + } + + ssize_t link_size; + while (1) { + link_size = readlink(path, buf, size); + if (link_size < 0) { + goto fail1; + } + if (link_size >= 0 && link_size < size) { + break; + } + if (size > SIZE_MAX / 2 || 2 * size > SIZE_MAX - 1) { + goto fail1; + } + size *= 2; + char *new_buf = BRealloc(buf, size + 1); + if (!new_buf) { + goto fail1; + } + buf = new_buf; + } + + buf[link_size] = '\0'; + return buf; + +fail1: + BFree(buf); +fail0: + return NULL; +} + +static char * find_module_library (NCDModuleInst *i, const char *module_name) +{ + char *ret = NULL; + + char *self = x_read_link("/proc/self/exe"); + if (!self) { + ModuleLog(i, BLOG_ERROR, "failed to read /proc/self/exe"); + goto fail0; + } + + char *slash = strrchr(self, '/'); + if (!slash) { + ModuleLog(i, BLOG_ERROR, "contents of /proc/self/exe do not have a slash"); + goto fail1; + } + *slash = '\0'; + + const char *paths[] = {"../lib/badvpn-ncd", "../mcvpn", NULL}; + + size_t j; + for (j = 0; paths[j]; j++) { + char *module_path = concat_strings(6, self, "/", paths[j], "/libncdmodule_", module_name, ".so"); + if (!module_path) { + ModuleLog(i, BLOG_ERROR, "concat_strings failed"); + goto fail1; + } + + if (access(module_path, F_OK) == 0) { + ret = module_path; + break; + } + + BFree(module_path); + } + + if (!paths[j]) { + ModuleLog(i, BLOG_ERROR, "failed to find module"); + } + +fail1: + BFree(self); +fail0: + return ret; +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + group->group_state = g; + LinkedList0_Init(&g->modules_list); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + + LinkedList0Node *ln; + while ((ln = LinkedList0_GetFirst(&g->modules_list))) { + struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node); + module_free(mod, g); + } + + BFree(g); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct module *mod = find_module(NCDVal_StringData(name_arg), ModuleGlobal(i)); + ASSERT(!mod || mod->lib_handle) + + if (!mod) { + mod = module_init(NCDVal_StringData(name_arg), i); + if (!mod) { + ModuleLog(i, BLOG_ERROR, "module_init failed"); + goto fail0; + } + + // find module library + char *module_path = find_module_library(i, NCDVal_StringData(name_arg)); + if (!module_path) { + module_free(mod, ModuleGlobal(i)); + goto fail0; + } + + // load it as a dynamic library + mod->lib_handle = dlopen(module_path, RTLD_NOW); + BFree(module_path); + if (!mod->lib_handle) { + ModuleLog(i, BLOG_ERROR, "dlopen failed"); + module_free(mod, ModuleGlobal(i)); + goto fail0; + } + } + + if (!mod->ncdmodule_loaded) { + // build name of NCDModuleGroup structure symbol + char *group_symbol = concat_strings(2, "ncdmodule_", NCDVal_StringData(name_arg)); + if (!group_symbol) { + ModuleLog(i, BLOG_ERROR, "concat_strings failed"); + goto fail0; + } + + // resolve NCDModuleGroup structure symbol + void *group = dlsym(mod->lib_handle, group_symbol); + BFree(group_symbol); + if (!group) { + ModuleLog(i, BLOG_ERROR, "dlsym failed"); + goto fail0; + } + + // load module group + if (!NCDModuleInst_Backend_InterpLoadGroup(i, (struct NCDModuleGroup *)group)) { + ModuleLog(i, BLOG_ERROR, "NCDModuleInst_Backend_InterpLoadGroup failed"); + goto fail0; + } + + mod->ncdmodule_loaded = 1; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "load_module", + .func_new2 = func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_load_module = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/log.c b/app/src/main/jni/badvpn/ncd/modules/log.c new file mode 100644 index 0000000..5b4251d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/log.c @@ -0,0 +1,285 @@ +/** + * @file log.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Message logging using the BLog system provided by the BadVPN framework. + * Each message has an associated loglevel, which must be one of: "error, "warning", + * "notice", "info", "debug", or a numeric identifier (1=error to 5=debug). + * + * Synopsis: + * log(string level [, string ...]) + * + * Description: + * On init, logs the concatenation of the given strings. + * + * Synopsis: + * log_r(string level [, string ...]) + * + * Description: + * On deinit, logs the concatenation of the given strings. + * + * Synopsis: + * log_fr(string level, list(string) strings_init, list(string) strings_deinit) + * + * Description: + * On init, logs the concatenation of the strings in 'strings_init', + * and on deinit, logs the concatenation of the strings in 'strings_deinit'. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct rlog_instance { + NCDModuleInst *i; + int level; + NCDValRef list; + size_t start; +}; + +enum {STRING_ERROR, STRING_WARNING, STRING_NOTICE, STRING_INFO, STRING_DEBUG}; + +static const char *strings[] = { + "error", "warning", "notice", "info", "debug", NULL +}; + +static int check_strings (NCDValRef list, size_t start) +{ + ASSERT(NCDVal_IsList(list)) + + size_t count = NCDVal_ListCount(list); + + for (size_t j = start; j < count; j++) { + NCDValRef string = NCDVal_ListGet(list, j); + if (!NCDVal_IsString(string)) { + return 0; + } + } + + return 1; +} + +static void do_log (int level, NCDValRef list, size_t start) +{ + ASSERT(level >= BLOG_ERROR) + ASSERT(level <= BLOG_DEBUG) + ASSERT(check_strings(list, start)) + + if (!BLog_WouldLog(BLOG_CHANNEL_ncd_log_msg, level)) { + return; + } + + size_t count = NCDVal_ListCount(list); + + BLog_Begin(); + + for (size_t j = start; j < count; j++) { + NCDValRef string = NCDVal_ListGet(list, j); + ASSERT(NCDVal_IsString(string)) + BLog_AppendBytes(NCDVal_StringData(string), NCDVal_StringLength(string)); + } + + BLog_Finish(BLOG_CHANNEL_ncd_log_msg, level); +} + +static int parse_level (NCDModuleInst *i, NCDValRef level_arg, int *out_level) +{ + if (!NCDVal_IsString(level_arg)) { + return 0; + } + + NCDStringIndex *string_index = i->params->iparams->string_index; + + uintmax_t level_numeric; + if (ncd_read_uintmax(level_arg, &level_numeric) && level_numeric >= BLOG_ERROR && level_numeric <= BLOG_DEBUG) { + *out_level = level_numeric; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_ERROR), string_index)) { + *out_level = BLOG_ERROR; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_WARNING), string_index)) { + *out_level = BLOG_WARNING; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_NOTICE), string_index)) { + *out_level = BLOG_NOTICE; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_INFO), string_index)) { + *out_level = BLOG_INFO; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_DEBUG), string_index)) { + *out_level = BLOG_DEBUG; + } + else { + return 0; + } + + return 1; +} + +static void rlog_func_new_common (void *vo, NCDModuleInst *i, int level, NCDValRef list, size_t start) +{ + ASSERT(level >= BLOG_ERROR) + ASSERT(level <= BLOG_DEBUG) + ASSERT(check_strings(list, start)) + + struct rlog_instance *o = vo; + o->i = i; + o->level = level; + o->list = list; + o->start = start; + + NCDModuleInst_Backend_Up(i); +} + +static void rlog_func_die (void *vo) +{ + struct rlog_instance *o = vo; + + do_log(o->level, o->list, o->start); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void log_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (NCDVal_ListCount(params->args) < 1) { + ModuleLog(i, BLOG_ERROR, "missing level argument"); + goto fail0; + } + + int level; + if (!parse_level(i, NCDVal_ListGet(params->args, 0), &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!check_strings(params->args, 1)) { + ModuleLog(i, BLOG_ERROR, "wrong string arguments"); + goto fail0; + } + + do_log(level, params->args, 1); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void log_r_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (NCDVal_ListCount(params->args) < 1) { + ModuleLog(i, BLOG_ERROR, "missing level argument"); + goto fail0; + } + + int level; + if (!parse_level(i, NCDVal_ListGet(params->args, 0), &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!check_strings(params->args, 1)) { + ModuleLog(i, BLOG_ERROR, "wrong string arguments"); + goto fail0; + } + + rlog_func_new_common(vo, i, level, params->args, 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void log_fr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef level_arg; + NCDValRef strings_init_arg; + NCDValRef strings_deinit_arg; + if (!NCDVal_ListRead(params->args, 3, &level_arg, &strings_init_arg, &strings_deinit_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + int level; + if (!parse_level(i, level_arg, &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!NCDVal_IsList(strings_init_arg) || !check_strings(strings_init_arg, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong string_init argument"); + goto fail0; + } + + if (!NCDVal_IsList(strings_deinit_arg) || !check_strings(strings_deinit_arg, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong strings_deinit argument"); + goto fail0; + } + + do_log(level, strings_init_arg, 0); + + rlog_func_new_common(vo, i, level, strings_deinit_arg, 0); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "log", + .func_new2 = log_func_new + }, { + .type = "log_r", + .func_new2 = log_r_func_new, + .func_die = rlog_func_die, + .alloc_size = sizeof(struct rlog_instance) + }, { + .type = "log_fr", + .func_new2 = log_fr_func_new, + .func_die = rlog_func_die, + .alloc_size = sizeof(struct rlog_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_log = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/logical.c b/app/src/main/jni/badvpn/ncd/modules/logical.c new file mode 100644 index 0000000..8ac6660 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/logical.c @@ -0,0 +1,160 @@ +/** + * @file logical.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for logical operators. + * + * Synopsis: not(string val) + * Variables: + * string (empty) - "true" if val does not equal "true", "false" otherwise + * + * Synopsis: or([string val1, ...]) + * Variables: + * string (empty) - "true" if at least one of the values equals "true", "false" otherwise + * + * Synopsis: and([string val1, ...]) + * Variables: + * string (empty) - "true" if all of the values equal "true", "false" otherwise + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_not, int is_or) +{ + struct instance *o = vo; + o->i = i; + + // compute value from arguments + if (is_not) { + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->value = !ncd_read_boolean(arg); + } else { + o->value = (is_or ? 0 : 1); + + size_t count = NCDVal_ListCount(params->args); + + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(params->args, j); + + if (!NCDVal_IsString(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + int this_value = ncd_read_boolean(arg); + if (is_or) { + o->value = o->value || this_value; + } else { + o->value = o->value && this_value; + } + } + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_not (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 1, 0); +} + +static void func_new_or (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 0, 1); +} + +static void func_new_and (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 0, 0); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "not", + .func_new2 = func_new_not, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "or", + .func_new2 = func_new_or, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "and", + .func_new2 = func_new_and, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_logical = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/modules.h b/app/src/main/jni/badvpn/ncd/modules/modules.h new file mode 100644 index 0000000..ea39027 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/modules.h @@ -0,0 +1,210 @@ +/** + * @file modules.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_MODULES_MODULES_H +#define BADVPN_NCD_MODULES_MODULES_H + +#include + +#include + +extern const struct NCDModuleGroup ncdmodule_var; +extern const struct NCDModuleGroup ncdmodule_list; +extern const struct NCDModuleGroup ncdmodule_depend; +extern const struct NCDModuleGroup ncdmodule_multidepend; +extern const struct NCDModuleGroup ncdmodule_dynamic_depend; +extern const struct NCDModuleGroup ncdmodule_concat; +extern const struct NCDModuleGroup ncdmodule_if; +extern const struct NCDModuleGroup ncdmodule_strcmp; +extern const struct NCDModuleGroup ncdmodule_logical; +extern const struct NCDModuleGroup ncdmodule_sleep; +extern const struct NCDModuleGroup ncdmodule_print; +extern const struct NCDModuleGroup ncdmodule_blocker; +extern const struct NCDModuleGroup ncdmodule_spawn; +extern const struct NCDModuleGroup ncdmodule_imperative; +extern const struct NCDModuleGroup ncdmodule_ref; +extern const struct NCDModuleGroup ncdmodule_index; +extern const struct NCDModuleGroup ncdmodule_alias; +extern const struct NCDModuleGroup ncdmodule_process_manager; +extern const struct NCDModuleGroup ncdmodule_ondemand; +extern const struct NCDModuleGroup ncdmodule_foreach; +extern const struct NCDModuleGroup ncdmodule_choose; +extern const struct NCDModuleGroup ncdmodule_from_string; +extern const struct NCDModuleGroup ncdmodule_to_string; +extern const struct NCDModuleGroup ncdmodule_value; +extern const struct NCDModuleGroup ncdmodule_try; +extern const struct NCDModuleGroup ncdmodule_exit; +extern const struct NCDModuleGroup ncdmodule_getargs; +extern const struct NCDModuleGroup ncdmodule_arithmetic; +extern const struct NCDModuleGroup ncdmodule_parse; +extern const struct NCDModuleGroup ncdmodule_valuemetic; +extern const struct NCDModuleGroup ncdmodule_file; +extern const struct NCDModuleGroup ncdmodule_netmask; +extern const struct NCDModuleGroup ncdmodule_implode; +extern const struct NCDModuleGroup ncdmodule_call2; +extern const struct NCDModuleGroup ncdmodule_assert; +extern const struct NCDModuleGroup ncdmodule_explode; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network; +extern const struct NCDModuleGroup ncdmodule_timer; +extern const struct NCDModuleGroup ncdmodule_file_open; +extern const struct NCDModuleGroup ncdmodule_backtrack; +extern const struct NCDModuleGroup ncdmodule_depend_scope; +extern const struct NCDModuleGroup ncdmodule_substr; +extern const struct NCDModuleGroup ncdmodule_log; +extern const struct NCDModuleGroup ncdmodule_buffer; +extern const struct NCDModuleGroup ncdmodule_getenv; +#ifndef BADVPN_EMSCRIPTEN +extern const struct NCDModuleGroup ncdmodule_regex_match; +extern const struct NCDModuleGroup ncdmodule_run; +extern const struct NCDModuleGroup ncdmodule_runonce; +extern const struct NCDModuleGroup ncdmodule_daemon; +extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice; +extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink; +extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn; +extern const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant; +#ifdef BADVPN_USE_LINUX_RFKILL +extern const struct NCDModuleGroup ncdmodule_net_backend_rfkill; +#endif +extern const struct NCDModuleGroup ncdmodule_net_up; +extern const struct NCDModuleGroup ncdmodule_net_dns; +extern const struct NCDModuleGroup ncdmodule_net_iptables; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_route; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_arp_probe; +extern const struct NCDModuleGroup ncdmodule_net_watch_interfaces; +extern const struct NCDModuleGroup ncdmodule_sys_watch_input; +extern const struct NCDModuleGroup ncdmodule_sys_watch_usb; +#ifdef BADVPN_USE_LINUX_INPUT +extern const struct NCDModuleGroup ncdmodule_sys_evdev; +#endif +#ifdef BADVPN_USE_INOTIFY +extern const struct NCDModuleGroup ncdmodule_sys_watch_directory; +#endif +extern const struct NCDModuleGroup ncdmodule_sys_request_server; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_wait_dynamic_addr; +extern const struct NCDModuleGroup ncdmodule_sys_request_client; +extern const struct NCDModuleGroup ncdmodule_reboot; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_route; +extern const struct NCDModuleGroup ncdmodule_socket; +extern const struct NCDModuleGroup ncdmodule_sys_start_process; +extern const struct NCDModuleGroup ncdmodule_load_module; +#endif + +static const struct NCDModuleGroup *ncd_modules[] = { + &ncdmodule_var, + &ncdmodule_list, + &ncdmodule_depend, + &ncdmodule_multidepend, + &ncdmodule_dynamic_depend, + &ncdmodule_concat, + &ncdmodule_if, + &ncdmodule_strcmp, + &ncdmodule_logical, + &ncdmodule_sleep, + &ncdmodule_print, + &ncdmodule_blocker, + &ncdmodule_spawn, + &ncdmodule_imperative, + &ncdmodule_ref, + &ncdmodule_index, + &ncdmodule_alias, + &ncdmodule_process_manager, + &ncdmodule_ondemand, + &ncdmodule_foreach, + &ncdmodule_choose, + &ncdmodule_from_string, + &ncdmodule_to_string, + &ncdmodule_value, + &ncdmodule_try, + &ncdmodule_exit, + &ncdmodule_getargs, + &ncdmodule_arithmetic, + &ncdmodule_parse, + &ncdmodule_valuemetic, + &ncdmodule_file, + &ncdmodule_netmask, + &ncdmodule_implode, + &ncdmodule_call2, + &ncdmodule_assert, + &ncdmodule_explode, + &ncdmodule_net_ipv4_addr_in_network, + &ncdmodule_net_ipv6_addr_in_network, + &ncdmodule_timer, + &ncdmodule_file_open, + &ncdmodule_backtrack, + &ncdmodule_depend_scope, + &ncdmodule_substr, + &ncdmodule_log, + &ncdmodule_buffer, + &ncdmodule_getenv, +#ifndef BADVPN_EMSCRIPTEN + &ncdmodule_regex_match, + &ncdmodule_run, + &ncdmodule_runonce, + &ncdmodule_daemon, + &ncdmodule_net_backend_waitdevice, + &ncdmodule_net_backend_waitlink, + &ncdmodule_net_backend_badvpn, + &ncdmodule_net_backend_wpa_supplicant, +#ifdef BADVPN_USE_LINUX_RFKILL + &ncdmodule_net_backend_rfkill, +#endif + &ncdmodule_net_up, + &ncdmodule_net_dns, + &ncdmodule_net_iptables, + &ncdmodule_net_ipv4_addr, + &ncdmodule_net_ipv4_route, + &ncdmodule_net_ipv4_dhcp, + &ncdmodule_net_ipv4_arp_probe, + &ncdmodule_net_watch_interfaces, + &ncdmodule_sys_watch_input, + &ncdmodule_sys_watch_usb, +#ifdef BADVPN_USE_LINUX_INPUT + &ncdmodule_sys_evdev, +#endif +#ifdef BADVPN_USE_INOTIFY + &ncdmodule_sys_watch_directory, +#endif + &ncdmodule_sys_request_server, + &ncdmodule_net_ipv6_wait_dynamic_addr, + &ncdmodule_sys_request_client, + &ncdmodule_reboot, + &ncdmodule_net_ipv6_addr, + &ncdmodule_net_ipv6_route, + &ncdmodule_socket, + &ncdmodule_sys_start_process, + &ncdmodule_load_module, +#endif + NULL +}; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/modules/multidepend.c b/app/src/main/jni/badvpn/ncd/modules/multidepend.c new file mode 100644 index 0000000..9b201ae --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/multidepend.c @@ -0,0 +1,401 @@ +/** + * @file multidepend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * This is a compatibility module. It behaves exactly like the depend_scope module, + * except that there is a single global scope for dependency names. + * + * Use depend_scope instead. If you are using multidepend between non-template + * processes, make those processes templates instead and start them via + * process_manager(). For example, instead of this: + * + * process foo { + * multiprovide("FOO"); + * } + * + * process bar { + * multidepend({"FOO"}); + * } + * + * Use this: + * + * process main { + * depend_scope() scope; + * process_manager() mgr; + * mgr->start("foo", "foo", {}); + * mgr->start("bar", "bar", {}); + * } + * + * template foo { + * _caller.scope->provide("FOO"); + * } + * + * template bar { + * _caller.scope->depend({"FOO"}); + * } + * + * Synopsis: + * multiprovide(name) + * + * Synopsis: + * multidepend(list names) + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide { + NCDModuleInst *i; + NCDValRef name; + LinkedList1Node provides_list_node; + LinkedList1 depends_list; + int dying; +}; + +struct depend { + NCDModuleInst *i; + NCDValRef names; + LinkedList1Node depends_list_node; + struct provide *provide; + LinkedList1Node provide_depends_list_node; + int provide_collapsing; +}; + +struct global { + LinkedList1 provides_list; + LinkedList1 depends_list; +}; + +static struct provide * find_provide (struct global *g, NCDValRef name) +{ + for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->provides_list); ln; ln = LinkedList1Node_Next(ln)) { + struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node); + if (NCDVal_Compare(provide->name, name) == 0) { + return provide; + } + } + + return NULL; +} + +static struct provide * depend_find_best_provide (struct depend *o) +{ + struct global *g = ModuleGlobal(o->i); + + size_t count = NCDVal_ListCount(o->names); + + for (size_t j = 0; j < count; j++) { + NCDValRef name = NCDVal_ListGet(o->names, j); + struct provide *provide = find_provide(g, name); + if (provide && !provide->dying) { + return provide; + } + } + + return NULL; +} + +static void depend_update (struct depend *o) +{ + // if we're collapsing, do nothing + if (o->provide && o->provide_collapsing) { + return; + } + + // find best provide + struct provide *best_provide = depend_find_best_provide(o); + ASSERT(!best_provide || !best_provide->dying) + + // has anything changed? + if (best_provide == o->provide) { + return; + } + + if (o->provide) { + // set collapsing + o->provide_collapsing = 1; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + // insert to provide's list + LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node); + + // set not collapsing + o->provide_collapsing = 0; + + // set provide + o->provide = best_provide; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init provides list + LinkedList1_Init(&g->provides_list); + + // init depends list + LinkedList1_Init(&g->depends_list); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->depends_list)) + ASSERT(LinkedList1_IsEmpty(&g->provides_list)) + + // free global state structure + BFree(g); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // remember name + o->name = name_arg; + + // check for existing provide with this name + if (find_provide(g, o->name)) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // insert to provides list + LinkedList1_Append(&g->provides_list, &o->provides_list_node); + + // init depends list + LinkedList1_Init(&o->depends_list); + + // set not dying + o->dying = 0; + + // signal up. + // This comes above the loop which follows, so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // update depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node); + depend_update(depend); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + // remove from provides list + LinkedList1_Remove(&g->provides_list, &o->provides_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(!o->dying) + + // if we have no depends, die immediately + if (LinkedList1_IsEmpty(&o->depends_list)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // start collapsing our depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node); + ASSERT(depend->provide == o) + + // update depend to make sure it is collapsing + depend_update(depend); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef names_arg; + if (!NCDVal_ListRead(params->args, 1, &names_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(names_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // remember names + o->names = names_arg; + + // insert to depends list + LinkedList1_Append(&g->depends_list, &o->depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + struct global *g = ModuleGlobal(o->i); + + if (o->provide) { + // remove from provide's list + LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node); + + // if provide is dying and is empty, let it die + if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) { + provide_free(o->provide); + } + } + + // remove from depends list + LinkedList1_Remove(&g->depends_list, &o->depends_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + + if (!(o->provide && o->provide_collapsing)) { + return; + } + + // save provide + struct provide *provide = o->provide; + + // remove from provide's list + LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + + // if provide is dying and is empty, let it die + if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) { + provide_free(provide); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + + if (!o->provide) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "multiprovide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "multidepend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_multidepend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_backend_badvpn.c b/app/src/main/jni/badvpn/ncd/modules/net_backend_badvpn.c new file mode 100644 index 0000000..572ae71 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_backend_badvpn.c @@ -0,0 +1,281 @@ +/** + * @file net_backend_badvpn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * BadVPN interface module. + * + * Synopsis: net.backend.badvpn(string ifname, string user, string exec, list(string) args) + */ + +#include +#include + +#include +#include +#include + +#include + +#define RETRY_TIME 5000 + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + const char *user; + size_t user_len; + const char *exec; + size_t exec_len; + NCDValRef args; + int dying; + int started; + BTimer timer; + BProcess process; +}; + +static void try_process (struct instance *o); +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status); +static void timer_handler (struct instance *o); +static void instance_free (struct instance *o); + +void try_process (struct instance *o) +{ + CmdLine c; + if (!CmdLine_Init(&c)) { + goto fail0; + } + + // append exec + if (!CmdLine_AppendNoNull(&c, o->exec, o->exec_len)) { + goto fail1; + } + + // append tapdev + if (!CmdLine_Append(&c, "--tapdev") || !CmdLine_Append(&c, o->ifname_nts.data)) { + goto fail1; + } + + // append arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + if (!CmdLine_AppendNoNull(&c, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + goto fail1; + } + } + + // terminate cmdline + if (!CmdLine_Finish(&c)) { + goto fail1; + } + + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, ((char **)c.arr.v)[0], (char **)c.arr.v, o->user)) { + ModuleLog(o->i, BLOG_ERROR, "BProcess_Init failed"); + goto fail1; + } + + CmdLine_Free(&c); + + // set started + o->started = 1; + + return; + +fail1: + CmdLine_Free(&c); +fail0: + // retry + o->started = 0; + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->started) + + ModuleLog(o->i, BLOG_INFO, "process terminated"); + + // free process + BProcess_Free(&o->process); + + // set not started + o->started = 0; + + if (o->dying) { + instance_free(o); + return; + } + + // set timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +void timer_handler (struct instance *o) +{ + ASSERT(!o->started) + + ModuleLog(o->i, BLOG_INFO, "retrying"); + + // try starting process again + try_process(o); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef user_arg; + NCDValRef exec_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &user_arg, &exec_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(user_arg) || + !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->user = NCDVal_StringData(user_arg); + o->user_len = NCDVal_StringLength(user_arg); + o->exec = NCDVal_StringData(exec_arg); + o->exec_len = NCDVal_StringLength(exec_arg); + o->args = args_arg; + + // check arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // create TAP device + if (!NCDIfConfig_make_tuntap(o->ifname_nts.data, o->user, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to create TAP device"); + goto fail1; + } + + // set device up + if (!NCDIfConfig_set_up(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set device up"); + goto fail2; + } + + // set not dying + o->dying = 0; + + // init timer + BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o); + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // try starting process + try_process(o); + return; + +fail2: + if (!NCDIfConfig_remove_tuntap(o->ifname_nts.data, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove TAP device"); + } +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(!o->started) + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + // set device down + if (!NCDIfConfig_set_down(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set device down"); + } + + // free TAP device + if (!NCDIfConfig_remove_tuntap(o->ifname_nts.data, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove TAP device"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + if (!o->started) { + instance_free(o); + return; + } + + // request termination + BProcess_Terminate(&o->process); + + // remember dying + o->dying = 1; +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.badvpn", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_badvpn = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_backend_rfkill.c b/app/src/main/jni/badvpn/ncd/modules/net_backend_rfkill.c new file mode 100644 index 0000000..311d973 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_backend_rfkill.c @@ -0,0 +1,216 @@ +/** + * @file net_backend_rfkill.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Rfkill monitoring module. + * + * Synopsis: net.backend.rfkill(string type, string name) + * Arguments: + * type - method of determining the index of the rfkill device. "index" for + * rfkill device index, "wlan" for wireless device. Be aware that, for + * the wireless device method, the index is resloved at initialization, + * and no attempt is made to refresh it if the device goes away. In other + * words, you should probably put a "net.backend.waitdevice" statement + * in front of the rfkill statement. + * name - rfkill index or wireless device name + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + uint32_t index; + NCDRfkillMonitor monitor; + int up; +}; + +static int find_wlan_rfill (const char *ifname, uint32_t *out_index) +{ + char ieee_path[100]; + snprintf(ieee_path, sizeof(ieee_path), "/sys/class/net/%s/../../ieee80211", ifname); + + int res = 0; + + DIR *d = opendir(ieee_path); + if (!d) { + goto fail0; + } + + struct dirent *e; + while (e = readdir(d)) { + if (!string_begins_with(e->d_name, "phy")) { + continue; + } + + char phy_path[150]; + snprintf(phy_path, sizeof(phy_path), "%s/%s", ieee_path, e->d_name); + + DIR *d2 = opendir(phy_path); + if (!d2) { + continue; + } + + struct dirent *e2; + while (e2 = readdir(d2)) { + int index_pos; + if (!(index_pos = string_begins_with(e2->d_name, "rfkill"))) { + continue; + } + + uint32_t index; + if (sscanf(e2->d_name + index_pos, "%"SCNu32, &index) != 1) { + continue; + } + + res = 1; + *out_index = index; + } + + closedir(d2); + } + + closedir(d); +fail0: + return res; +} + +static void monitor_handler (struct instance *o, struct rfkill_event event) +{ + if (event.idx != o->index) { + return; + } + + int was_up = o->up; + o->up = (event.op != RFKILL_OP_DEL && !event.soft && !event.hard); + + if (o->up && !was_up) { + NCDModuleInst_Backend_Up(o->i); + } + else if (!o->up && was_up) { + NCDModuleInst_Backend_Down(o->i); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef type_arg; + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 2, &type_arg, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(type_arg) || !NCDVal_IsStringNoNulls(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate name + NCDValNullTermString name_nts; + if (!NCDVal_StringNullTerminate(name_arg, &name_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_StringEquals(type_arg, "index")) { + if (sscanf(name_nts.data, "%"SCNu32, &o->index) != 1) { + ModuleLog(o->i, BLOG_ERROR, "wrong index argument"); + goto fail1; + } + } + else if (NCDVal_StringEquals(type_arg, "wlan")) { + if (!find_wlan_rfill(name_nts.data, &o->index)) { + ModuleLog(o->i, BLOG_ERROR, "failed to find rfkill for wlan interface"); + goto fail1; + } + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown type argument"); + goto fail1; + } + + // init monitor + if (!NCDRfkillMonitor_Init(&o->monitor, o->i->params->iparams->reactor, (NCDRfkillMonitor_handler)monitor_handler, o)) { + ModuleLog(o->i, BLOG_ERROR, "monitor failed"); + goto fail1; + } + + // set not up + o->up = 0; + + // free name nts + NCDValNullTermString_Free(&name_nts); + return; + +fail1: + NCDValNullTermString_Free(&name_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free monitor + NCDRfkillMonitor_Free(&o->monitor); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.rfkill", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_rfkill = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_backend_waitdevice.c b/app/src/main/jni/badvpn/ncd/modules/net_backend_waitdevice.c new file mode 100644 index 0000000..6ed99f6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_backend_waitdevice.c @@ -0,0 +1,187 @@ +/** + * @file net_backend_waitdevice.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which waits for the presence of a network interface. + * + * Synopsis: net.backend.waitdevice(string ifname) + * Description: statement is UP when a network interface named ifname + * exists, and DOWN when it does not. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define DEVPATH_REGEX "/net/[^/]+$" + +struct instance { + NCDModuleInst *i; + const char *ifname; + size_t ifname_len; + NCDUdevClient client; + regex_t reg; + char *devpath; + uintmax_t ifindex; +}; + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + if (o->devpath && !strcmp(devpath, o->devpath) && !NCDUdevManager_Query(o->i->params->iparams->umanager, o->devpath)) { + // free devpath + free(o->devpath); + + // set no devpath + o->devpath = NULL; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + if (!cache_map) { + goto out; + } + + int match_res = regexec(&o->reg, devpath, 0, NULL, 0); + const char *interface = BStringMap_Get(cache_map, "INTERFACE"); + const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX"); + + uintmax_t ifindex; + if (!(!match_res && interface && strlen(interface) == o->ifname_len && !memcmp(interface, o->ifname, o->ifname_len) && ifindex_str && parse_unsigned_integer(ifindex_str, &ifindex))) { + goto out; + } + + if (o->devpath && (strcmp(o->devpath, devpath) || o->ifindex != ifindex)) { + // free devpath + free(o->devpath); + + // set no devpath + o->devpath = NULL; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + if (!o->devpath) { + // grab devpath + o->devpath = devpath; + devpath = NULL; + + // remember ifindex + o->ifindex = ifindex; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->ifname = NCDVal_StringData(arg); + o->ifname_len = NCDVal_StringLength(arg); + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // compile regex + if (regcomp(&o->reg, DEVPATH_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail1; + } + + // set no devpath + o->devpath = NULL; + return; + +fail1: + NCDUdevClient_Free(&o->client); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free devpath + if (o->devpath) { + free(o->devpath); + } + + // free regex + regfree(&o->reg); + + // free client + NCDUdevClient_Free(&o->client); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.waitdevice", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_waitdevice = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_backend_waitlink.c b/app/src/main/jni/badvpn/ncd/modules/net_backend_waitlink.c new file mode 100644 index 0000000..4ea54e8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_backend_waitlink.c @@ -0,0 +1,155 @@ +/** + * @file net_backend_waitlink.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which waits for the link on a network interface. + * + * Synopsis: net.backend.waitlink(string ifname) + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDInterfaceMonitor monitor; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_event event) +{ + ASSERT(event.event == NCDIFMONITOR_EVENT_LINK_UP || event.event == NCDIFMONITOR_EVENT_LINK_DOWN) + + int was_up = o->up; + o->up = (event.event == NCDIFMONITOR_EVENT_LINK_UP); + + if (o->up && !was_up) { + NCDModuleInst_Backend_Up(o->i); + } + else if (!o->up && was_up) { + NCDModuleInst_Backend_Down(o->i); + } +} + +static void monitor_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "monitor error"); + + instance_free(o, 1); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // get interface index + int ifindex; + int res = badvpn_get_iface_info(ifname_nts.data, NULL, NULL, &ifindex); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to get interface index"); + goto fail0; + } + + // init monitor + if (!NCDInterfaceMonitor_Init(&o->monitor, ifindex, NCDIFMONITOR_WATCH_LINK, i->params->iparams->reactor, o, (NCDInterfaceMonitor_handler)monitor_handler, (NCDInterfaceMonitor_handler_error)monitor_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed"); + goto fail0; + } + + // set not up + o->up = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free monitor + NCDInterfaceMonitor_Free(&o->monitor); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.waitlink", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_waitlink = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_backend_wpa_supplicant.c b/app/src/main/jni/badvpn/ncd/modules/net_backend_wpa_supplicant.c new file mode 100644 index 0000000..c92f5d7 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_backend_wpa_supplicant.c @@ -0,0 +1,563 @@ +/** + * @file net_backend_wpa_supplicant.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Wireless interface module which runs wpa_supplicant to connect to a wireless network. + * + * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to + * start if the switch is of when it is started, and will stop working indefinitely if the + * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill" + * statement in front of the wpa_supplicant statement. + * + * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args) + * Variables: + * bssid - BSSID of the wireless network we connected to, or "none". + * Consists of 6 capital, two-character hexadecimal numbers, separated with colons. + * Example: "01:B2:C3:04:E5:F6" + * ssid - SSID of the wireless network we connected to. Note that this is after what + * wpa_supplicant does to it before it prints it. In particular, it replaces all bytes + * outside [32, 126] with underscores. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_LINE_LEN 512 +#define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED" +#define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED" + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + const char *ifname; + size_t ifname_len; + const char *conf; + size_t conf_len; + const char *exec; + size_t exec_len; + NCDValRef args; + int dying; + int up; + BInputProcess process; + int have_pipe; + LineBuffer pipe_buffer; + PacketPassInterface pipe_input; + int have_info; + int info_have_bssid; + uint8_t info_bssid[6]; + char *info_ssid; +}; + +static int parse_hex_digit (uint8_t d, uint8_t *out); +static int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len); +static int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len); +static int build_cmdline (struct instance *o, CmdLine *c); +static int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len); +static void free_info (struct instance *o); +static void process_error (struct instance *o); +static void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status); +static void process_handler_closed (struct instance *o, int is_error); +static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len); +static void instance_free (struct instance *o, int is_error); + +int parse_hex_digit (uint8_t d, uint8_t *out) +{ + switch (d) { + case '0': *out = 0; return 1; + case '1': *out = 1; return 1; + case '2': *out = 2; return 1; + case '3': *out = 3; return 1; + case '4': *out = 4; return 1; + case '5': *out = 5; return 1; + case '6': *out = 6; return 1; + case '7': *out = 7; return 1; + case '8': *out = 8; return 1; + case '9': *out = 9; return 1; + case 'A': case 'a': *out = 10; return 1; + case 'B': case 'b': *out = 11; return 1; + case 'C': case 'c': *out = 12; return 1; + case 'D': case 'd': *out = 13; return 1; + case 'E': case 'e': *out = 14; return 1; + case 'F': case 'f': *out = 15; return 1; + } + + return 0; +} + +int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len) +{ + // Trying to associate with AB:CD:EF:01:23:45 (SSID='Some SSID' freq=2462 MHz) + + int p; + if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with "))) { + return 0; + } + data += p; + data_len -= p; + + for (int i = 0; i < 6; i++) { + uint8_t d1; + uint8_t d2; + if (data_len < 2 || !parse_hex_digit(data[0], &d1) || !parse_hex_digit(data[1], &d2)) { + return 0; + } + data += 2; + data_len -= 2; + out_bssid[i] = ((d1 << 4) | d2); + + if (i != 5) { + if (data_len < 1 || data[0] != ':') { + return 0; + } + data += 1; + data_len -= 1; + } + } + + if (!(p = data_begins_with((char *)data, data_len, " (SSID='"))) { + return 0; + } + data += p; + data_len -= p; + + // find last ' + uint8_t *q = NULL; + for (int i = data_len; i > 0; i--) { + if (data[i - 1] == '\'') { + q = &data[i - 1]; + break; + } + } + if (!q) { + return 0; + } + + *out_ssid = data; + *out_ssid_len = q - data; + + return 1; +} + +int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len) +{ + // Trying to associate with SSID 'Some SSID' + + int p; + if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with SSID '"))) { + return 0; + } + data += p; + data_len -= p; + + // find last ' + uint8_t *q = NULL; + for (int i = data_len; i > 0; i--) { + if (data[i - 1] == '\'') { + q = &data[i - 1]; + break; + } + } + if (!q) { + return 0; + } + + *out_ssid = data; + *out_ssid_len = q - data; + + return 1; +} + +int build_cmdline (struct instance *o, CmdLine *c) +{ + // init cmdline + if (!CmdLine_Init(c)) { + goto fail0; + } + + // append stdbuf part + if (!build_stdbuf_cmdline(c, o->exec, o->exec_len)) { + goto fail1; + } + + // append user arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // append argument + if (!CmdLine_AppendNoNull(c, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + goto fail1; + } + } + + // append interface name + if (!CmdLine_Append(c, "-i") || !CmdLine_AppendNoNull(c, o->ifname, o->ifname_len)) { + goto fail1; + } + + // append config file + if (!CmdLine_Append(c, "-c") || !CmdLine_AppendNoNull(c, o->conf, o->conf_len)) { + goto fail1; + } + + // terminate cmdline + if (!CmdLine_Finish(c)) { + goto fail1; + } + + return 1; + +fail1: + CmdLine_Free(c); +fail0: + return 0; +} + +int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len) +{ + ASSERT(!o->have_info) + + // set bssid + o->info_have_bssid = have_bssid; + if (have_bssid) { + memcpy(o->info_bssid, bssid, 6); + } + + // set ssid + if (!(o->info_ssid = BAllocSize(bsize_add(bsize_fromsize(ssid_len), bsize_fromsize(1))))) { + ModuleLog(o->i, BLOG_ERROR, "BAllocSize failed"); + return 0; + } + memcpy(o->info_ssid, ssid, ssid_len); + o->info_ssid[ssid_len] = '\0'; + + // set have info + o->have_info = 1; + + return 1; +} + +void free_info (struct instance *o) +{ + ASSERT(o->have_info) + + // free ssid + BFree(o->info_ssid); + + // set not have info + o->have_info = 0; +} + +void process_error (struct instance *o) +{ + BInputProcess_Terminate(&o->process); +} + +void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated"); + + // die + instance_free(o, !o->dying); + return; +} + +void process_handler_closed (struct instance *o, int is_error) +{ + ASSERT(o->have_pipe) + + if (is_error) { + ModuleLog(o->i, BLOG_ERROR, "pipe error"); + } else { + ModuleLog(o->i, BLOG_INFO, "pipe closed"); + } + + // free buffer + LineBuffer_Free(&o->pipe_buffer); + + // free input interface + PacketPassInterface_Free(&o->pipe_input); + + // set have no pipe + o->have_pipe = 0; +} + +void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len) +{ + ASSERT(o->have_pipe) + ASSERT(data_len > 0) + + // accept packet + PacketPassInterface_Done(&o->pipe_input); + + if (o->dying) { + return; + } + + // strip "interface: " from beginning of line. Older wpa_supplicant versions (<1.0) don't add this + // prefix, so don't fail if there isn't one. + size_t l1; + size_t l2; + if (o->ifname_len > 0 && (l1 = data_begins_with_bin((char *)data, data_len, o->ifname, o->ifname_len)) && (l2 = data_begins_with((char *)data + l1, data_len - l1, ": "))) { + data += l1 + l2; + data_len -= l1 + l2; + } + + int have_bssid = 1; + uint8_t bssid[6]; + uint8_t *ssid; + int ssid_len; + if (parse_trying(data, data_len, bssid, &ssid, &ssid_len) || (have_bssid = 0, parse_trying_nobssid(data, data_len, &ssid, &ssid_len))) { + ModuleLog(o->i, BLOG_INFO, "trying event"); + + if (o->up) { + ModuleLog(o->i, BLOG_ERROR, "trying unexpected!"); + process_error(o); + return; + } + + if (o->have_info) { + free_info(o); + } + + if (!init_info(o, have_bssid, bssid, ssid, ssid_len)) { + ModuleLog(o->i, BLOG_ERROR, "init_info failed"); + process_error(o); + return; + } + } + else if (data_begins_with((char *)data, data_len, EVENT_STRING_CONNECTED)) { + ModuleLog(o->i, BLOG_INFO, "connected event"); + + if (o->up || !o->have_info) { + ModuleLog(o->i, BLOG_ERROR, "connected unexpected!"); + process_error(o); + return; + } + + o->up = 1; + NCDModuleInst_Backend_Up(o->i); + } + else if (data_begins_with((char *)data, data_len, EVENT_STRING_DISCONNECTED)) { + ModuleLog(o->i, BLOG_INFO, "disconnected event"); + + if (o->have_info) { + free_info(o); + } + + if (o->up) { + o->up = 0; + NCDModuleInst_Backend_Down(o->i); + } + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef conf_arg; + NCDValRef exec_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(conf_arg) || + !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->ifname = NCDVal_StringData(ifname_arg); + o->ifname_len = NCDVal_StringLength(ifname_arg); + o->conf = NCDVal_StringData(conf_arg); + o->conf_len = NCDVal_StringLength(conf_arg); + o->exec = NCDVal_StringData(exec_arg); + o->exec_len = NCDVal_StringLength(exec_arg); + o->args = args_arg; + + // set not dying + o->dying = 0; + + // set not up + o->up = 0; + + // build process cmdline + CmdLine c; + if (!build_cmdline(o, &c)) { + ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline"); + goto fail0; + } + + // init process + if (!BInputProcess_Init(&o->process, o->i->params->iparams->reactor, o->i->params->iparams->manager, o, + (BInputProcess_handler_terminated)process_handler_terminated, + (BInputProcess_handler_closed)process_handler_closed + )) { + ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Init failed"); + goto fail1; + } + + // init input interface + PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->params->iparams->reactor)); + + // init buffer + if (!LineBuffer_Init(&o->pipe_buffer, BInputProcess_GetInput(&o->process), &o->pipe_input, MAX_LINE_LEN, '\n')) { + ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed"); + goto fail2; + } + + // set have pipe + o->have_pipe = 1; + + // start process + if (!BInputProcess_Start(&o->process, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL)) { + ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Start failed"); + goto fail3; + } + + // set not have info + o->have_info = 0; + + CmdLine_Free(&c); + return; + +fail3: + LineBuffer_Free(&o->pipe_buffer); +fail2: + PacketPassInterface_Free(&o->pipe_input); + BInputProcess_Free(&o->process); +fail1: + CmdLine_Free(&c); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // free info + if (o->have_info) { + free_info(o); + } + + if (o->have_pipe) { + // free buffer + LineBuffer_Free(&o->pipe_buffer); + + // free input interface + PacketPassInterface_Free(&o->pipe_input); + } + + // free process + BInputProcess_Free(&o->process); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // request termination + BInputProcess_Terminate(&o->process); + + // remember dying + o->dying = 1; +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + ASSERT(o->have_info) + + if (!strcmp(name, "bssid")) { + char str[18]; + + if (!o->info_have_bssid) { + sprintf(str, "none"); + } else { + uint8_t *id = o->info_bssid; + sprintf(str, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8, id[0], id[1], id[2], id[3], id[4], id[5]); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "ssid")) { + *out = NCDVal_NewString(mem, o->info_ssid); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.wpa_supplicant", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_dns.c b/app/src/main/jni/badvpn/ncd/modules/net_dns.c new file mode 100644 index 0000000..5bfdcd3 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_dns.c @@ -0,0 +1,315 @@ +/** + * @file net_dns.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DNS servers module. + * + * Synopsis: net.dns(list(string) servers, string priority) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct instance { + NCDModuleInst *i; + LinkedList1 ipv4_dns_servers; + LinkedList1Node instances_node; // node in instances +}; + +struct ipv4_dns_entry { + LinkedList1Node list_node; // node in instance.ipv4_dns_servers + uint32_t addr; + int priority; +}; + +struct global { + LinkedList1 instances; +}; + +static struct ipv4_dns_entry * add_ipv4_dns_entry (struct instance *o, uint32_t addr, int priority) +{ + // allocate entry + struct ipv4_dns_entry *entry = malloc(sizeof(*entry)); + if (!entry) { + return NULL; + } + + // set info + entry->addr = addr; + entry->priority = priority; + + // add to list + LinkedList1_Append(&o->ipv4_dns_servers, &entry->list_node); + + return entry; +} + +static void remove_ipv4_dns_entry (struct instance *o, struct ipv4_dns_entry *entry) +{ + // remove from list + LinkedList1_Remove(&o->ipv4_dns_servers, &entry->list_node); + + // free entry + free(entry); +} + +static void remove_ipv4_dns_entries (struct instance *o) +{ + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->ipv4_dns_servers)) { + struct ipv4_dns_entry *e = UPPER_OBJECT(n, struct ipv4_dns_entry, list_node); + remove_ipv4_dns_entry(o, e); + } +} + +static size_t num_servers (struct global *g) +{ + size_t c = 0; + + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) { + struct instance *o = UPPER_OBJECT(n, struct instance, instances_node); + for (LinkedList1Node *en = LinkedList1_GetFirst(&o->ipv4_dns_servers); en; en = LinkedList1Node_Next(en)) { + c++; + } + } + + return c; +} + +struct dns_sort_entry { + uint32_t addr; + int priority; +}; + +static int dns_sort_comparator (const void *v1, const void *v2) +{ + const struct dns_sort_entry *e1 = v1; + const struct dns_sort_entry *e2 = v2; + return B_COMPARE(e1->priority, e2->priority); +} + +static int set_servers (struct global *g) +{ + int ret = 0; + + // count servers + size_t num_ipv4_dns_servers = num_servers(g); + + // allocate sort array + struct dns_sort_entry *servers = BAllocArray(num_ipv4_dns_servers, sizeof(servers[0])); + if (!servers) { + goto fail0; + } + size_t num_servers = 0; + + // fill sort array + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) { + struct instance *o = UPPER_OBJECT(n, struct instance, instances_node); + for (LinkedList1Node *en = LinkedList1_GetFirst(&o->ipv4_dns_servers); en; en = LinkedList1Node_Next(en)) { + struct ipv4_dns_entry *e = UPPER_OBJECT(en, struct ipv4_dns_entry, list_node); + servers[num_servers].addr = e->addr; + servers[num_servers].priority= e->priority; + num_servers++; + } + } + ASSERT(num_servers == num_ipv4_dns_servers) + + // sort by priority + // use a custom insertion sort instead of qsort() because we want a stable sort + struct dns_sort_entry sort_temp; + BInsertionSort(servers, num_servers, sizeof(servers[0]), dns_sort_comparator, &sort_temp); + + // copy addresses into an array + uint32_t *addrs = BAllocArray(num_servers, sizeof(addrs[0])); + if (!addrs) { + goto fail1; + } + for (size_t i = 0; i < num_servers; i++) { + addrs[i] = servers[i].addr; + } + + // set servers + if (!NCDIfConfig_set_dns_servers(addrs, num_servers)) { + goto fail2; + } + + ret = 1; + +fail2: + BFree(addrs); +fail1: + BFree(servers); +fail0: + return ret; +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init instances list + LinkedList1_Init(&g->instances); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->instances)) + + // free global state structure + BFree(g); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct instance *o = vo; + o->i = i; + + // init servers list + LinkedList1_Init(&o->ipv4_dns_servers); + + // get arguments + NCDValRef servers_arg; + NCDValRef priority_arg; + if (!NCDVal_ListRead(params->args, 2, &servers_arg, &priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsList(servers_arg) || !NCDVal_IsString(priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + uintmax_t priority; + if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong priority"); + goto fail1; + } + + // read servers + size_t count = NCDVal_ListCount(servers_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef server_arg = NCDVal_ListGet(servers_arg, j); + + if (!NCDVal_IsString(server_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(server_arg), NCDVal_StringLength(server_arg), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong addr"); + goto fail1; + } + + if (!add_ipv4_dns_entry(o, addr, priority)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry"); + goto fail1; + } + } + + // add to instances + LinkedList1_Append(&g->instances, &o->instances_node); + + // set servers + if (!set_servers(g)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers"); + goto fail2; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail2: + LinkedList1_Remove(&g->instances, &o->instances_node); +fail1: + remove_ipv4_dns_entries(o); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + struct global *g = ModuleGlobal(o->i); + + // remove from instances + LinkedList1_Remove(&g->instances, &o->instances_node); + + // set servers + set_servers(g); + + // free servers + remove_ipv4_dns_entries(o); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.dns", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_dns = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_iptables.c b/app/src/main/jni/badvpn/ncd/modules/net_iptables.c new file mode 100644 index 0000000..a5af2ee --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_iptables.c @@ -0,0 +1,749 @@ +/** + * @file net_iptables.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * iptables and ebtables module. + * + * Note that all iptables/ebtables commands (in general) must be issued synchronously, or + * the kernel may randomly report errors if there is another iptables/ebtables command in + * progress. To solve this, the NCD process contains a single "iptables lock". All + * iptables/ebtables commands exposed here go through that lock. + * In case you wish to call iptables/ebtables directly, the lock is exposed via + * net.iptables.lock(). + * + * The append and insert commands, instead of using the variable-argument form below + * as documented below, may alternatively be called with a single list argument. + * + * Synopsis: + * net.iptables.append(string table, string chain, string arg1 ...) + * Description: + * init: iptables -t table -A chain arg1 ... + * deinit: iptables -t table -D chain arg1 ... + * + * Synopsis: + * net.iptables.insert(string table, string chain, string arg1 ...) + * Description: + * init: iptables -t table -I chain arg1 ... + * deinit: iptables -t table -D chain arg1 ... + * + * Synopsis: + * net.iptables.policy(string table, string chain, string target, string revert_target) + * Description: + * init: iptables -t table -P chain target + * deinit: iptables -t table -P chain revert_target + * + * Synopsis: + * net.iptables.newchain(string table, string chain) + * net.iptables.newchain(string chain) // DEPRECATED, defaults to table="filter" + * Description: + * init: iptables -t table -N chain + * deinit: iptables -t table -X chain + * + * Synopsis: + * net.ebtables.append(string table, string chain, string arg1 ...) + * Description: + * init: ebtables -t table -A chain arg1 ... + * deinit: ebtables -t table -D chain arg1 ... + * + * Synopsis: + * net.ebtables.insert(string table, string chain, string arg1 ...) + * Description: + * init: ebtables -t table -I chain arg1 ... + * deinit: ebtables -t table -D chain arg1 ... + * + * Synopsis: + * net.ebtables.policy(string table, string chain, string target, string revert_target) + * Description: + * init: ebtables -t table -P chain target + * deinit: ebtables -t table -P chain revert_target + * + * Synopsis: + * net.ebtables.newchain(string table, string chain) + * Description: + * init: ebtables -t table -N chain + * deinit: ebtables -t table -X chain + * + * Synopsis: + * net.iptables.lock() + * Description: + * Use at the beginning of a block of custom iptables/ebtables commands to make sure + * they do not interfere with other iptables/ebtables commands. + * WARNING: improper usage of the lock can lead to deadlock. In particular: + * - Do not call any of the iptables/ebtables wrappers above from a lock section; + * those will attempt to aquire the lock themselves. + * - Do not enter another lock section from a lock section. + * - Do not perform any potentially long wait from a lock section. + * + * Synopsis: + * net.iptables.lock::unlock() + * Description: + * Use at the end of a block of custom iptables/ebtables commands to make sure + * they do not interfere with other iptables/ebtables commands. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +static void template_free_func (void *vo, int is_error); + +struct global { + BEventLock iptables_lock; +}; + +struct instance { + NCDModuleInst *i; + command_template_instance cti; +}; + +struct unlock_instance; + +#define LOCK_STATE_LOCKING 1 +#define LOCK_STATE_LOCKED 2 +#define LOCK_STATE_UNLOCKED 3 +#define LOCK_STATE_RELOCKING 4 + +struct lock_instance { + NCDModuleInst *i; + BEventLockJob lock_job; + struct unlock_instance *unlock; + int state; +}; + +struct unlock_instance { + NCDModuleInst *i; + struct lock_instance *lock; +}; + +static void unlock_free (struct unlock_instance *o); + +static int build_append_or_insert_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl, const char *type) +{ + if (NCDVal_ListRead(args, 1, &args) && !NCDVal_IsList(args)) { + ModuleLog(i, BLOG_ERROR, "in one-argument form a list is expected"); + goto fail0; + } + + // read arguments + NCDValRef table_arg; + NCDValRef chain_arg; + if (!NCDVal_ListReadHead(args, 2, &table_arg, &chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(table_arg) || !NCDVal_IsStringNoNulls(chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = NCDVal_StringData(table_arg); + size_t table_len = NCDVal_StringLength(table_arg); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec) || !CmdLine_Append(cl, "-t") || !CmdLine_AppendNoNull(cl, table, table_len) || !CmdLine_Append(cl, (remove ? "-D" : type)) || !CmdLine_AppendNoNull(cl, chain, chain_len)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + size_t count = NCDVal_ListCount(args); + for (size_t j = 2; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(args, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + if (!CmdLine_AppendNoNull(cl, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendNoNull failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_append_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + return build_append_or_insert_cmdline(i, args, prog, remove, exec, cl, "-A"); +} + +static int build_insert_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + return build_append_or_insert_cmdline(i, args, prog, remove, exec, cl, "-I"); +} + +static int build_policy_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef table_arg; + NCDValRef chain_arg; + NCDValRef target_arg; + NCDValRef revert_target_arg; + if (!NCDVal_ListRead(args, 4, &table_arg, &chain_arg, &target_arg, &revert_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(table_arg) || !NCDVal_IsStringNoNulls(chain_arg) || + !NCDVal_IsStringNoNulls(target_arg) || !NCDVal_IsStringNoNulls(revert_target_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = NCDVal_StringData(table_arg); + size_t table_len = NCDVal_StringLength(table_arg); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + const char *target = NCDVal_StringData(target_arg); + size_t target_len = NCDVal_StringLength(target_arg); + const char *revert_target = NCDVal_StringData(revert_target_arg); + size_t revert_target_len = NCDVal_StringLength(revert_target_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add arguments + if (!CmdLine_Append(cl, *exec) || !CmdLine_Append(cl, "-t") || !CmdLine_AppendNoNull(cl, table, table_len) || + !CmdLine_Append(cl, "-P") || !CmdLine_AppendNoNull(cl, chain, chain_len) || + !CmdLine_AppendNoNull(cl, (remove ? revert_target : target), (remove ? revert_target_len : target_len)) + ) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_newchain_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef table_arg = NCDVal_NewInvalid(); + NCDValRef chain_arg; + if (!NCDVal_ListRead(args, 1, &chain_arg) && !NCDVal_ListRead(args, 2, &table_arg, &chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if ((!NCDVal_IsInvalid(table_arg) && !NCDVal_IsStringNoNulls(table_arg)) || !NCDVal_IsStringNoNulls(chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = (NCDVal_IsInvalid(table_arg) ? "filter" : NCDVal_StringData(table_arg)); + size_t table_len = (NCDVal_IsInvalid(table_arg) ? 6 : NCDVal_StringLength(table_arg)); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add arguments + if (!CmdLine_AppendMulti(cl, 2, *exec, "-t") || + !CmdLine_AppendNoNull(cl, table, table_len) || + !CmdLine_Append(cl, (remove ? "-X" : "-N")) || + !CmdLine_AppendNoNull(cl, chain, chain_len) + ) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_iptables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_ip6tables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ebtables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static void lock_job_handler (struct lock_instance *o) +{ + ASSERT(o->state == LOCK_STATE_LOCKING || o->state == LOCK_STATE_RELOCKING) + + if (o->state == LOCK_STATE_LOCKING) { + ASSERT(!o->unlock) + + // up + NCDModuleInst_Backend_Up(o->i); + + // set state locked + o->state = LOCK_STATE_LOCKED; + } + else if (o->state == LOCK_STATE_RELOCKING) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + + // die unlock + unlock_free(o->unlock); + o->unlock = NULL; + + // set state locked + o->state = LOCK_STATE_LOCKED; + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init iptables lock + BEventLock_Init(&g->iptables_lock, BReactor_PendingGroup(params->reactor)); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + + // free iptables lock + BEventLock_Free(&g->iptables_lock); + + // free global state structure + BFree(g); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline) +{ + struct global *g = ModuleGlobal(i); + struct instance *o = vo; + o->i = i; + + command_template_new(&o->cti, i, params, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &g->iptables_lock); +} + +void template_free_func (void *vo, int is_error) +{ + struct instance *o = vo; + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void append_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_append_cmdline); +} + +static void insert_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_insert_cmdline); +} + +static void policy_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_policy_cmdline); +} + +static void newchain_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_newchain_cmdline); +} + +static void append_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_append_cmdline); +} + +static void insert_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_insert_cmdline); +} + +static void policy_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_policy_cmdline); +} + +static void newchain_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_newchain_cmdline); +} + +static void append_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_append_cmdline); +} + +static void insert_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_insert_cmdline); +} + +static void policy_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_policy_cmdline); +} + +static void newchain_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_newchain_cmdline); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + command_template_die(&o->cti); +} + +static void lock_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct lock_instance *o = vo; + o->i = i; + + // init lock job + BEventLockJob_Init(&o->lock_job, &g->iptables_lock, (BEventLock_handler)lock_job_handler, o); + BEventLockJob_Wait(&o->lock_job); + + // set no unlock + o->unlock = NULL; + + // set state locking + o->state = LOCK_STATE_LOCKING; +} + +static void lock_func_die (void *vo) +{ + struct lock_instance *o = vo; + + if (o->state == LOCK_STATE_UNLOCKED) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + o->unlock->lock = NULL; + } + else if (o->state == LOCK_STATE_RELOCKING) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + unlock_free(o->unlock); + } + else { + ASSERT(!o->unlock) + } + + // free lock job + BEventLockJob_Free(&o->lock_job); + + // dead + NCDModuleInst_Backend_Dead(o->i); +} + +static void unlock_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct unlock_instance *o = vo; + o->i = i; + + // get lock lock + struct lock_instance *lock = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure lock doesn't already have an unlock + if (lock->unlock) { + BLog(BLOG_ERROR, "lock already has an unlock"); + goto fail0; + } + + // make sure lock is locked + if (lock->state != LOCK_STATE_LOCKED) { + BLog(BLOG_ERROR, "lock is not locked"); + goto fail0; + } + + // set lock + o->lock = lock; + + // set unlock in lock + lock->unlock = o; + + // up + NCDModuleInst_Backend_Up(o->i); + + // release lock + BEventLockJob_Release(&lock->lock_job); + + // set lock state unlocked + lock->state = LOCK_STATE_UNLOCKED; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void unlock_func_die (void *vo) +{ + struct unlock_instance *o = vo; + + // if lock is gone, die right away + if (!o->lock) { + unlock_free(o); + return; + } + + ASSERT(o->lock->unlock == o) + ASSERT(o->lock->state == LOCK_STATE_UNLOCKED) + + // wait lock + BEventLockJob_Wait(&o->lock->lock_job); + + // set lock state relocking + o->lock->state = LOCK_STATE_RELOCKING; +} + +static void unlock_free (struct unlock_instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.iptables.append", + .func_new2 = append_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.insert", + .func_new2 = insert_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.policy", + .func_new2 = policy_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.newchain", + .func_new2 = newchain_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.append", + .func_new2 = append_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.insert", + .func_new2 = insert_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.policy", + .func_new2 = policy_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.newchain", + .func_new2 = newchain_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.append", + .func_new2 = append_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.insert", + .func_new2 = insert_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.policy", + .func_new2 = policy_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.newchain", + .func_new2 = newchain_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.lock", + .func_new2 = lock_func_new, + .func_die = lock_func_die, + .alloc_size = sizeof(struct lock_instance) + }, { + .type = "net.iptables.lock::unlock", + .func_new2 = unlock_func_new, + .func_die = unlock_func_die, + .alloc_size = sizeof(struct unlock_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_iptables = { + .modules = modules, + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr.c new file mode 100644 index 0000000..14eaea4 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr.c @@ -0,0 +1,148 @@ +/** + * @file net_ipv4_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv4 address module. + * + * Synopsis: + * net.ipv4.addr(string ifname, string addr, string prefix) + * net.ipv4.addr(string ifname, string cidr_addr) + * + * Description: + * Adds the given address to the given network interface on initialization, + * and removes it on deinitialization. The second form takes the address and + * prefix in CIDR notation (a.b.c.d/n). + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + struct ipv4_ifaddr ifaddr; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef addr_arg; + NCDValRef prefix_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &ifname_arg, &addr_arg) && + !NCDVal_ListRead(params->args, 3, &ifname_arg, &addr_arg, &prefix_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsString(addr_arg) || + (!NCDVal_IsInvalid(prefix_arg) && !NCDVal_IsString(prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_IsInvalid(prefix_arg)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation address"); + goto fail1; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail1; + } + + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &o->ifaddr.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong prefix"); + goto fail1; + } + } + + // add address + if (!NCDIfConfig_add_ipv4_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add IP address"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove address + if (!NCDIfConfig_remove_ipv4_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove IP address"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.addr", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_addr = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr_in_network.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr_in_network.c new file mode 100644 index 0000000..fa63811 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_addr_in_network.c @@ -0,0 +1,173 @@ +/** + * @file net_ipv4_addr_in_network.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv4.addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv4.addr_in_network(string addr, string cidr_net_addr) + * net.ipv4.ifnot_addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv4.ifnot_addr_in_network(string addr, string cidr_net_addr) + * + * Description: + * Checks if two IPv4 addresses belong to the same subnet. + * The prefix length is given either in the a separate argument or along with + * the second address in CIDR notation (address/prefix). + * This can be used to check whether an address belongs to a certain + * subnet, hence the name. + * + * Variables: + * (empty) - "true" if addresses belong to the same subnet, "false" if not + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_ifnot) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_addr; + NCDValRef arg_net_addr; + NCDValRef arg_net_prefix = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &arg_addr, &arg_net_addr) && + !NCDVal_ListRead(params->args, 3, &arg_addr, &arg_net_addr, &arg_net_prefix) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_addr) || !NCDVal_IsString(arg_net_addr) || + (!NCDVal_IsInvalid(arg_net_prefix) && !NCDVal_IsString(arg_net_prefix)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad address"); + goto fail0; + } + + // parse network + struct ipv4_ifaddr network; + if (NCDVal_IsInvalid(arg_net_prefix)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network)) { + ModuleLog(o->i, BLOG_ERROR, "bad network in CIDR notation"); + goto fail0; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network.addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad network address"); + goto fail0; + } + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(arg_net_prefix), NCDVal_StringLength(arg_net_prefix), &network.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "bad network prefix"); + goto fail0; + } + } + + // test + o->value = ipaddr_ipv4_addrs_in_network(addr, network.addr, network.prefix); + + if (is_ifnot && o->value) { + ModuleLog(o->i, BLOG_ERROR, "addresses belong to same subnet, not proceeding"); + } + + // signal up + if (!is_ifnot || !o->value) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_normal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 0); +} + +static void func_new_ifnot (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 1); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.addr_in_network", + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "ip_in_network", // compatibility name + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ipv4.ifnot_addr_in_network", + .func_new2 = func_new_ifnot, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv4_arp_probe.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_arp_probe.c new file mode 100644 index 0000000..6f8e45e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_arp_probe.c @@ -0,0 +1,212 @@ +/** + * @file net_ipv4_arp_probe.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * ARP probing module. + * + * Synopsis: + * net.ipv4.arp_probe(string ifname, string addr) + * + * Description: + * Monitors local presence of an IPv4 host on a network interface. + * On initialization, may take some time to determine whether + * the host is present or not, then goes to UP state. When it + * determines that presence has changed, toggles itself DOWN then + * UP to expose the new determination. + * + * Variables: + * exists - "true" if the host exists, "false" if not + */ + +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_UNKNOWN 1 +#define STATE_EXIST 2 +#define STATE_NOEXIST 3 + +struct instance { + NCDModuleInst *i; + BArpProbe arpprobe; + int state; +}; + +static void instance_free (struct instance *o, int is_error); + +static void arpprobe_handler (struct instance *o, int event) +{ + switch (event) { + case BARPPROBE_EVENT_EXIST: { + ASSERT(o->state == STATE_UNKNOWN || o->state == STATE_NOEXIST) + + ModuleLog(o->i, BLOG_INFO, "exist"); + + if (o->state == STATE_NOEXIST) { + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state exist + o->state = STATE_EXIST; + } break; + + case BARPPROBE_EVENT_NOEXIST: { + ASSERT(o->state == STATE_UNKNOWN || o->state == STATE_EXIST) + + ModuleLog(o->i, BLOG_INFO, "noexist"); + + if (o->state == STATE_EXIST) { + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state noexist + o->state = STATE_NOEXIST; + } break; + + case BARPPROBE_EVENT_ERROR: { + ModuleLog(o->i, BLOG_ERROR, "error"); + + // die + instance_free(o, 1); + return; + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_ifname; + NCDValRef arg_addr; + if (!NCDVal_ListRead(params->args, 2, &arg_ifname, &arg_addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(arg_ifname) || !NCDVal_IsString(arg_addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse address + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(arg_ifname, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // init arpprobe + int res = BArpProbe_Init(&o->arpprobe, ifname_nts.data, addr, i->params->iparams->reactor, o, (BArpProbe_handler)arpprobe_handler); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BArpProbe_Init failed"); + goto fail0; + } + + // set state unknown + o->state = STATE_UNKNOWN; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free arpprobe + BArpProbe_Free(&o->arpprobe); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_EXIST || o->state == STATE_NOEXIST) + + if (!strcmp(name, "exists")) { + *out = ncd_make_boolean(mem, o->state == STATE_EXIST, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.arp_probe", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_arp_probe = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv4_dhcp.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_dhcp.c new file mode 100644 index 0000000..d9e8b74 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_dhcp.c @@ -0,0 +1,351 @@ +/** + * @file net_ipv4_dhcp.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client module. + * + * Synopsis: + * net.ipv4.dhcp(string ifname [, list opts]) + * + * Description: + * Runs a DHCP client on a network interface. When an address is obtained, + * transitions up (but does not assign anything). If the lease times out, + * transitions down. + * The interface must already be up. + * Supported options (in the opts argument): + * - "hostname", (string value): send this hostname to the DHCP server + * - "vendorclassid", (string value): send this vendor class identifier + * - "auto_clientid": send a client identifier generated from the MAC address + * + * Variables: + * string addr - assigned IP address ("A.B.C.D") + * string prefix - address prefix length ("N") + * string cidr_addr - address and prefix in CIDR notation ("A.B.C.D/N") + * string gateway - router address ("A.B.C.D"), or "none" if not provided + * list(string) dns_servers - DNS server addresses ("A.B.C.D" ...) + * string server_mac - MAC address of the DHCP server (6 two-digit caps hexadecimal values + * separated with colons, e.g."AB:CD:EF:01:02:03") + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + BDHCPClient dhcp; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void dhcp_handler (struct instance *o, int event) +{ + switch (event) { + case BDHCPCLIENT_EVENT_UP: { + ASSERT(!o->up) + o->up = 1; + NCDModuleInst_Backend_Up(o->i); + } break; + + case BDHCPCLIENT_EVENT_DOWN: { + ASSERT(o->up) + o->up = 0; + NCDModuleInst_Backend_Down(o->i); + } break; + + case BDHCPCLIENT_EVENT_ERROR: { + instance_free(o, 1); + return; + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ifname_arg; + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &ifname_arg) && !NCDVal_ListRead(params->args, 2, &ifname_arg, &opts_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || (!NCDVal_IsInvalid(opts_arg) && !NCDVal_IsList(opts_arg))) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValNullTermString hostname_nts = NCDValNullTermString_NewDummy(); + NCDValNullTermString vendorclassid_nts = NCDValNullTermString_NewDummy(); + + struct BDHCPClient_opts opts = {}; + + // read options + size_t count = NCDVal_IsInvalid(opts_arg) ? 0 : NCDVal_ListCount(opts_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef opt = NCDVal_ListGet(opts_arg, j); + + // read name + if (!NCDVal_IsString(opt)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option name type"); + goto fail1; + } + + if (NCDVal_StringEquals(opt, "hostname") || NCDVal_StringEquals(opt, "vendorclassid")) { + int is_hostname = NCDVal_StringEquals(opt, "hostname"); + + // read value + if (j == count) { + ModuleLog(o->i, BLOG_ERROR, "option value missing"); + goto fail1; + } + NCDValRef val = NCDVal_ListGet(opts_arg, j + 1); + if (!NCDVal_IsStringNoNulls(val)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option value type"); + goto fail1; + } + + // null terminate + NCDValNullTermString nts; + if (!NCDVal_StringNullTerminate(val, &nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + NCDValNullTermString *nts_ptr = (is_hostname ? &hostname_nts : &vendorclassid_nts); + NCDValNullTermString_Free(nts_ptr); + *nts_ptr = nts; + + if (is_hostname) { + opts.hostname = nts.data; + } else { + opts.vendorclassid = nts.data; + } + + j++; + } + else if (NCDVal_StringEquals(opt, "auto_clientid")) { + opts.auto_clientid = 1; + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown option name"); + goto fail1; + } + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + + // init DHCP + int res = BDHCPClient_Init(&o->dhcp, ifname_nts.data, opts, o->i->params->iparams->reactor, o->i->params->iparams->random2, (BDHCPClient_handler)dhcp_handler, o); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BDHCPClient_Init failed"); + goto fail1; + } + + // set not up + o->up = 0; + + // free options nts's + NCDValNullTermString_Free(&hostname_nts); + NCDValNullTermString_Free(&vendorclassid_nts); + return; + +fail1: + NCDValNullTermString_Free(&hostname_nts); + NCDValNullTermString_Free(&vendorclassid_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free DHCP + BDHCPClient_Free(&o->dhcp); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + + if (!strcmp(name, "addr")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + + char str[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr, str); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "prefix")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + uint32_t mask; + BDHCPClient_GetClientMask(&o->dhcp, &mask); + + struct ipv4_ifaddr ifaddr; + if (!ipaddr_ipv4_ifaddr_from_addr_mask(addr, mask, &ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "bad netmask"); + return 0; + } + + char str[10]; + sprintf(str, "%d", ifaddr.prefix); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "cidr_addr")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + uint32_t mask; + BDHCPClient_GetClientMask(&o->dhcp, &mask); + + struct ipv4_ifaddr ifaddr; + if (!ipaddr_ipv4_ifaddr_from_addr_mask(addr, mask, &ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "bad netmask"); + return 0; + } + + char str[IPADDR_PRINT_MAX]; + ipaddr_print_ifaddr(ifaddr, str); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "gateway")) { + char str[IPADDR_PRINT_MAX]; + + uint32_t addr; + if (!BDHCPClient_GetRouter(&o->dhcp, &addr)) { + strcpy(str, "none"); + } else { + ipaddr_print_addr(addr, str); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "dns_servers")) { + uint32_t servers[BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS]; + int num_servers = BDHCPClient_GetDNS(&o->dhcp, servers, BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS); + + *out = NCDVal_NewList(mem, num_servers); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (int i = 0; i < num_servers; i++) { + char str[IPADDR_PRINT_MAX]; + ipaddr_print_addr(servers[i], str); + + NCDValRef server = NCDVal_NewString(mem, str); + if (NCDVal_IsInvalid(server)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, server)) { + goto fail; + } + } + + return 1; + } + + if (!strcmp(name, "server_mac")) { + uint8_t mac[6]; + BDHCPClient_GetServerMAC(&o->dhcp, mac); + + char str[18]; + sprintf(str, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.dhcp", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv4_route.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_route.c new file mode 100644 index 0000000..0f34388 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv4_route.c @@ -0,0 +1,211 @@ +/** + * @file net_ipv4_route.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv4 route module. + * + * Synopsis: + * net.ipv4.route(string dest, string dest_prefix, string gateway, string metric, string ifname) + * net.ipv4.route(string cidr_dest, string gateway, string metric, string ifname) + * + * Description: + * Adds an IPv4 route to the system's routing table on initiailzation, and + * removes it on deinitialization. The second form takes the destination in + * CIDR notation (a.b.c.d/n). + * If 'gateway' is "none", the route will only be associated with an interface. + * If 'gateway' is "blackhole", the route will be a blackhole route (and 'ifname' is unused). + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define TYPE_NORMAL 1 +#define TYPE_IFONLY 2 +#define TYPE_BLACKHOLE 3 + +struct instance { + NCDModuleInst *i; + struct ipv4_ifaddr dest; + int type; + uint32_t gateway; + int metric; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef dest_arg; + NCDValRef dest_prefix_arg = NCDVal_NewInvalid(); + NCDValRef gateway_arg; + NCDValRef metric_arg; + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 4, &dest_arg, &gateway_arg, &metric_arg, &ifname_arg) && + !NCDVal_ListRead(params->args, 5, &dest_arg, &dest_prefix_arg, &gateway_arg, &metric_arg, &ifname_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(dest_arg) || !NCDVal_IsString(gateway_arg) || + !NCDVal_IsString(metric_arg) || !NCDVal_IsStringNoNulls(ifname_arg) || + (!NCDVal_IsInvalid(dest_prefix_arg) && !NCDVal_IsString(dest_prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read dest + if (NCDVal_IsInvalid(dest_prefix_arg)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation dest"); + goto fail0; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest addr"); + goto fail0; + } + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(dest_prefix_arg), NCDVal_StringLength(dest_prefix_arg), &o->dest.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest prefix"); + goto fail0; + } + } + + // read gateway and choose type + if (NCDVal_StringEquals(gateway_arg, "none")) { + o->type = TYPE_IFONLY; + } + else if (NCDVal_StringEquals(gateway_arg, "blackhole")) { + o->type = TYPE_BLACKHOLE; + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(gateway_arg), NCDVal_StringLength(gateway_arg), &o->gateway)) { + ModuleLog(o->i, BLOG_ERROR, "wrong gateway"); + goto fail0; + } + o->type = TYPE_NORMAL; + } + + // read metric + uintmax_t metric; + if (!ncd_read_uintmax(metric_arg, &metric) || metric > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "bad metric"); + goto fail0; + } + o->metric = metric; + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // add route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_add_ipv4_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_add_ipv4_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_add_ipv4_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to add route"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_remove_ipv4_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_remove_ipv4_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_remove_ipv4_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove route"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.route", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_route = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr.c new file mode 100644 index 0000000..debee66 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr.c @@ -0,0 +1,148 @@ +/** + * @file net_ipv6_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 address module. + * + * Synopsis: + * net.ipv6.addr(string ifname, string addr, string prefix) + * net.ipv6.addr(string ifname, string cidr_addr) + * + * Description: + * Adds the given address to the given network interface on initialization, + * and removes it on deinitialization. The second form takes the address and + * prefix in CIDR notation (a.b.c.d/n). + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + struct ipv6_ifaddr ifaddr; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef addr_arg; + NCDValRef prefix_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &ifname_arg, &addr_arg) && + !NCDVal_ListRead(params->args, 3, &ifname_arg, &addr_arg, &prefix_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsString(addr_arg) || + (!NCDVal_IsInvalid(prefix_arg) && !NCDVal_IsString(prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_IsInvalid(prefix_arg)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation address"); + goto fail1; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail1; + } + + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &o->ifaddr.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong prefix"); + goto fail1; + } + } + + // add address + if (!NCDIfConfig_add_ipv6_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add IP address"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove address + if (!NCDIfConfig_remove_ipv6_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove IP address"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.addr", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_addr = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr_in_network.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr_in_network.c new file mode 100644 index 0000000..1ae9677 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_addr_in_network.c @@ -0,0 +1,168 @@ +/** + * @file net_ipv6_addr_in_network.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv6.addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv6.addr_in_network(string addr, string cidr_net_addr) + * net.ipv6.ifnot_addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv6.ifnot_addr_in_network(string addr, string cidr_net_addr) + * + * Description: + * Checks if two IPv6 addresses belong to the same subnet. + * The prefix length is given either in the a separate argument or along with + * the second address in CIDR notation (address/prefix). + * This can be used to check whether an address belongs to a certain + * subnet, hence the name. + * + * Variables: + * (empty) - "true" if addresses belong to the same subnet, "false" if not + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_ifnot) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_addr; + NCDValRef arg_net_addr; + NCDValRef arg_net_prefix = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &arg_addr, &arg_net_addr) && + !NCDVal_ListRead(params->args, 3, &arg_addr, &arg_net_addr, &arg_net_prefix) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_addr) || !NCDVal_IsString(arg_net_addr) || + (!NCDVal_IsInvalid(arg_net_prefix) && !NCDVal_IsString(arg_net_prefix)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + struct ipv6_addr addr; + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad address"); + goto fail0; + } + + // parse network + struct ipv6_ifaddr network; + if (NCDVal_IsInvalid(arg_net_prefix)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network)) { + ModuleLog(o->i, BLOG_ERROR, "bad network in CIDR notation"); + goto fail0; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network.addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad network address"); + goto fail0; + } + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(arg_net_prefix), NCDVal_StringLength(arg_net_prefix), &network.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "bad network prefix"); + goto fail0; + } + } + + // test + o->value = ipaddr6_ipv6_addrs_in_network(addr, network.addr, network.prefix); + + if (is_ifnot && o->value) { + ModuleLog(o->i, BLOG_ERROR, "addresses belong to same subnet, not proceeding"); + } + + // signal up + if (!is_ifnot || !o->value) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_normal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 0); +} + +static void func_new_ifnot (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 1); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.addr_in_network", + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ipv6.ifnot_addr_in_network", + .func_new2 = func_new_ifnot, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv6_route.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_route.c new file mode 100644 index 0000000..2f4ed0b --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_route.c @@ -0,0 +1,213 @@ +/** + * @file net_ipv6_route.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 route module. + * + * Synopsis: + * net.ipv6.route(string dest, string dest_prefix, string gateway, string metric, string ifname) + * net.ipv6.route(string cidr_dest, string gateway, string metric, string ifname) + * + * Description: + * Adds an IPv6 route to the system's routing table on initiailzation, and + * removes it on deinitialization. The second form takes the destination in + * CIDR notation (address/prefix). + * If 'gateway' is "none", the route will only be associated with an interface. + * If 'gateway' is "blackhole", the route will be a blackhole route (and 'ifname' is unused). + * NOTE: blackhole routes for IPv6 are not yet implemented in Linux; + * adding them via this interface will only work once they + * have been. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define TYPE_NORMAL 1 +#define TYPE_IFONLY 2 +#define TYPE_BLACKHOLE 3 + +struct instance { + NCDModuleInst *i; + struct ipv6_ifaddr dest; + int type; + struct ipv6_addr gateway; + int metric; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef dest_arg; + NCDValRef dest_prefix_arg = NCDVal_NewInvalid(); + NCDValRef gateway_arg; + NCDValRef metric_arg; + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 4, &dest_arg, &gateway_arg, &metric_arg, &ifname_arg) && + !NCDVal_ListRead(params->args, 5, &dest_arg, &dest_prefix_arg, &gateway_arg, &metric_arg, &ifname_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(dest_arg) || !NCDVal_IsString(gateway_arg) || + !NCDVal_IsString(metric_arg) || !NCDVal_IsStringNoNulls(ifname_arg) || + (!NCDVal_IsInvalid(dest_prefix_arg) && !NCDVal_IsString(dest_prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read dest + if (NCDVal_IsInvalid(dest_prefix_arg)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation dest"); + goto fail0; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest addr"); + goto fail0; + } + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(dest_prefix_arg), NCDVal_StringLength(dest_prefix_arg), &o->dest.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest prefix"); + goto fail0; + } + } + + // read gateway and choose type + if (NCDVal_StringEquals(gateway_arg, "none")) { + o->type = TYPE_IFONLY; + } + else if (NCDVal_StringEquals(gateway_arg, "blackhole")) { + o->type = TYPE_BLACKHOLE; + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(gateway_arg), NCDVal_StringLength(gateway_arg), &o->gateway)) { + ModuleLog(o->i, BLOG_ERROR, "wrong gateway"); + goto fail0; + } + o->type = TYPE_NORMAL; + } + + // read metric + uintmax_t metric; + if (!ncd_read_uintmax(metric_arg, &metric) || metric > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "bad metric"); + goto fail0; + } + o->metric = metric; + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // add route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_add_ipv6_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_add_ipv6_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_add_ipv6_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to add route"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_remove_ipv6_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_remove_ipv6_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_remove_ipv6_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove route"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.route", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_route = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_ipv6_wait_dynamic_addr.c b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_wait_dynamic_addr.c new file mode 100644 index 0000000..f0404d5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_ipv6_wait_dynamic_addr.c @@ -0,0 +1,201 @@ +/** + * @file net_ipv6_wait_dynamic_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv6.wait_dynamic_addr(string ifname) + * + * Description: + * Waits for a dynamic IPv6 address to be obtained on the interface, + * and goes up when it is obtained. + * If the address is subsequently lost, goes back down and again waits + * for an address. + * + * Variables: + * string addr - dynamic address obtained on the interface + * string prefix - prefix length + * string cidr_addr - address and prefix (addr/prefix) + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDInterfaceMonitor monitor; + struct ipv6_ifaddr ifaddr; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_event event) +{ + if (!o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED && (event.u.ipv6_addr.addr_flags & NCDIFMONITOR_ADDR_FLAG_DYNAMIC)) { + // rememeber address, set up + o->ifaddr = event.u.ipv6_addr.addr; + o->up = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + else if (o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED && !memcmp(event.u.ipv6_addr.addr.addr.bytes, o->ifaddr.addr.bytes, 16) && event.u.ipv6_addr.addr.prefix == o->ifaddr.prefix) { + // set not up + o->up = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } +} + +static void monitor_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "monitor error"); + + instance_free(o, 1); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // get interface index + int ifindex; + int res = badvpn_get_iface_info(ifname_nts.data, NULL, NULL, &ifindex); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to get interface index"); + goto fail0; + } + + // init monitor + if (!NCDInterfaceMonitor_Init(&o->monitor, ifindex, NCDIFMONITOR_WATCH_IPV6_ADDR, i->params->iparams->reactor, o, (NCDInterfaceMonitor_handler)monitor_handler, (NCDInterfaceMonitor_handler_error)monitor_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed"); + goto fail0; + } + + // set not up + o->up = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free monitor + NCDInterfaceMonitor_Free(&o->monitor); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + + if (!strcmp(name, "addr")) { + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(o->ifaddr.addr, str); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "prefix")) { + char str[10]; + sprintf(str, "%d", o->ifaddr.prefix); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "cidr_addr")) { + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_ifaddr(o->ifaddr, str); + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.wait_dynamic_addr", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_wait_dynamic_addr = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_up.c b/app/src/main/jni/badvpn/ncd/modules/net_up.c new file mode 100644 index 0000000..3aa04ce --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_up.c @@ -0,0 +1,119 @@ +/** + * @file net_up.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network interface up and down module. + * + * Synopsis: net.up(string ifname) + * Description: Sets a network interface up on initialization and down on + * deinitialization. + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // set interface up + if (!NCDIfConfig_set_up(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set interface up"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // set interface down + if (!NCDIfConfig_set_down(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set interface down"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.up", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_up = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/net_watch_interfaces.c b/app/src/main/jni/badvpn/ncd/modules/net_watch_interfaces.c new file mode 100644 index 0000000..7bd7b70 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/net_watch_interfaces.c @@ -0,0 +1,474 @@ +/** + * @file net_watch_interfaces.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network interface watcher. + * + * Synopsis: net.watch_interfaces() + * Description: reports network interface events. Transitions up when an event is detected, and + * goes down waiting for the next event when net.watch_interfaces::nextevent() is called. + * On startup, "added" events are reported for existing interfaces. + * Variables: + * string event_type - what happened with the interface: "added" or "removed". This may not be + * consistent across events. + * string devname - interface name + * string bus - bus location, for example "pci:0000:06:00.0", "usb:2-1.3:1.0", or "unknown" + * + * Synopsis: net.watch_interfaces::nextevent() + * Description: makes the watch_interfaces module transition down in order to report the next event. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define DEVPATH_REGEX "/net/[^/]+$" +#define DEVPATH_USB_REGEX "/usb[^/]*(/[^/]+)+/([^/]+)/net/[^/]+$" +#define DEVPATH_PCI_REGEX "/pci[^/]*/[^/]+/([^/]+)/net/[^/]+$" + +struct device { + char *ifname; + char *devpath; + uintmax_t ifindex; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + NCDUdevClient client; + LinkedList1 devices_list; + regex_t reg; + regex_t usb_reg; + regex_t pci_reg; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_ifname (struct instance *o, const char *ifname) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->ifname, ifname)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free ifname + free(device->ifname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *ifname, const char *bus, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set ifname + if (!BStringMap_Set(&map, "devname", ifname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set bus + if (!BStringMap_Set(&map, "bus", bus)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *ifname, const char *devpath, uintmax_t ifindex, const char *bus) +{ + ASSERT(!find_device_by_ifname(o, ifname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init ifname + if (!(device->ifname = strdup(ifname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // set ifindex + device->ifindex = ifindex; + + // init removed map + if (!make_event_map(o, 0, ifname, bus, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, ifname, bus, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->ifname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", ifname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void make_bus (struct instance *o, const char *devpath, const BStringMap *map, char *out_bus, size_t bus_avail) +{ + regmatch_t pmatch[3]; + + const char *type; + const char *id; + size_t id_len; + + if (!regexec(&o->usb_reg, devpath, 3, pmatch, 0)) { + type = "usb"; + id = devpath + pmatch[2].rm_so; + id_len = pmatch[2].rm_eo - pmatch[2].rm_so; + } + else if (!regexec(&o->pci_reg, devpath, 3, pmatch, 0)) { + type = "pci"; + id = devpath + pmatch[1].rm_so; + id_len = pmatch[1].rm_eo - pmatch[1].rm_so; + } else { + goto fail; + } + + size_t type_len = strlen(type); + bsize_t bus_len = bsize_add(bsize_fromsize(type_len), bsize_add(bsize_fromint(1), bsize_add(bsize_fromsize(id_len), bsize_fromint(1)))); + if (bus_len.is_overflow || bus_len.value > bus_avail) { + goto fail; + } + + memcpy(out_bus, type, type_len); + out_bus[type_len] = ':'; + memcpy(out_bus + type_len + 1, id, id_len); + out_bus[type_len + 1 + id_len] = '\0'; + return; + +fail: + snprintf(out_bus, bus_avail, "%s", "unknown"); +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + int match_res = regexec(&o->reg, devpath, 0, NULL, 0); + const char *interface = BStringMap_Get(cache_map, "INTERFACE"); + const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX"); + + uintmax_t ifindex; + if (!(!match_res && interface && ifindex_str && parse_unsigned_integer(ifindex_str, &ifindex))) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && (strcmp(ex_device->ifname, interface) || ex_device->ifindex != ifindex)) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_ifname_device = find_device_by_ifname(o, interface); + if (ex_ifname_device) { + remove_device(o, ex_ifname_device); + } + + char bus[128]; + make_bus(o, devpath, cache_map, bus, sizeof(bus)); + + add_device(o, interface, devpath, ifindex, bus); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + // compile regex's + if (regcomp(&o->reg, DEVPATH_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail1; + } + if (regcomp(&o->usb_reg, DEVPATH_USB_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail2; + } + if (regcomp(&o->pci_reg, DEVPATH_PCI_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail3; + } + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail3: + regfree(&o->usb_reg); +fail2: + regfree(&o->reg); +fail1: + NCDUdevClient_Free(&o->client); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->devices_list)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + free_device(o, device, 1); + } + + // free regex's + regfree(&o->pci_reg); + regfree(&o->usb_reg); + regfree(&o->reg); + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "net.watch_interfaces", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.watch_interfaces::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_watch_interfaces = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/netmask.c b/app/src/main/jni/badvpn/ncd/modules/netmask.c new file mode 100644 index 0000000..53d9398 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/netmask.c @@ -0,0 +1,263 @@ +/** + * @file netmask.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * ipv4_prefix_to_mask(string prefix) + * + * Variables: + * string (empty) - prefix, converted to dotted decimal format without leading + * zeros + * + * Synopsis: + * ipv4_mask_to_prefix(string mask) + * + * Variables: + * string (empty) - mask, converted to prefix length + * + * Synopsis: + * ipv4_net_from_addr_and_prefix(string addr, string prefix) + * + * Variables: + * string (empty) - network part of the address, according to given prefix + * length, in dotted decimal format without leading zeros + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct addr_instance { + NCDModuleInst *i; + uint32_t addr; +}; + +struct prefix_instance { + NCDModuleInst *i; + int prefix; +}; + +static void addr_func_init_templ (void *vo, NCDModuleInst *i, uint32_t addr) +{ + struct addr_instance *o = vo; + o->i = i; + + // remember address + o->addr = addr; + + // signal up + NCDModuleInst_Backend_Up(i); +} + +static void prefix_to_mask_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef prefix_arg; + if (!NCDVal_ListRead(params->args, 1, &prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse prefix + int prefix; + if (!ipaddr_parse_ipv4_prefix_bin((char *)NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &prefix)) { + ModuleLog(i, BLOG_ERROR, "bad prefix"); + goto fail0; + } + + // make mask + uint32_t mask = ipaddr_ipv4_mask_from_prefix(prefix); + + addr_func_init_templ(vo, i, mask); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ipv4_net_from_addr_and_prefix_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef addr_arg; + NCDValRef prefix_arg; + if (!NCDVal_ListRead(params->args, 2, &addr_arg, &prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(addr_arg) || !NCDVal_IsString(prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &addr)) { + ModuleLog(i, BLOG_ERROR, "bad addr"); + goto fail0; + } + + // parse prefix + int prefix; + if (!ipaddr_parse_ipv4_prefix_bin((char *)NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &prefix)) { + ModuleLog(i, BLOG_ERROR, "bad prefix"); + goto fail0; + } + + // make network + uint32_t network = (addr & ipaddr_ipv4_mask_from_prefix(prefix)); + + addr_func_init_templ(vo, i, network); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void addr_func_die (void *vo) +{ + struct addr_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct addr_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + char buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(o->addr, buf); + + *out = NCDVal_NewString(mem, buf); + return 1; + } + + return 0; +} + +static void mask_to_prefix_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct prefix_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef mask_arg; + if (!NCDVal_ListRead(params->args, 1, &mask_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(mask_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse mask + uint32_t mask; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(mask_arg), NCDVal_StringLength(mask_arg), &mask)) { + ModuleLog(i, BLOG_ERROR, "bad mask"); + goto fail0; + } + + // build prefix + if (!ipaddr_ipv4_prefix_from_mask(mask, &o->prefix)) { + ModuleLog(i, BLOG_ERROR, "bad mask"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void prefix_func_die (void *vo) +{ + struct prefix_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int prefix_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct prefix_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + char buf[6]; + sprintf(buf, "%d", o->prefix); + + *out = NCDVal_NewString(mem, buf); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "ipv4_prefix_to_mask", + .func_new2 = prefix_to_mask_func_init, + .func_die = addr_func_die, + .func_getvar2 = addr_func_getvar2, + .alloc_size = sizeof(struct addr_instance) + }, { + .type = "ipv4_mask_to_prefix", + .func_new2 = mask_to_prefix_func_init, + .func_die = prefix_func_die, + .func_getvar2 = prefix_func_getvar2, + .alloc_size = sizeof(struct prefix_instance) + }, { + .type = "ipv4_net_from_addr_and_prefix", + .func_new2 = ipv4_net_from_addr_and_prefix_func_init, + .func_die = addr_func_die, + .func_getvar2 = addr_func_getvar2, + .alloc_size = sizeof(struct addr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_netmask = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/ondemand.c b/app/src/main/jni/badvpn/ncd/modules/ondemand.c new file mode 100644 index 0000000..15c2531 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/ondemand.c @@ -0,0 +1,372 @@ +/** + * @file ondemand.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * On-demand process manager. + * + * Synopsis: + * ondemand(string template_name, list args) + * + * Description: + * Manages an on-demand template process using a process template named + * template_name. + * On deinitialization, if the process is running, reqests its termination + * and waits for it to terminate. + * + * Synopsis: + * ondemand::demand() + * + * Description: + * Demands the availability of an on-demand template process. + * This statement is in UP state if and only if the template process of the + * corresponding ondemand object is completely up. + * + * Variables: + * Exposes variables and objects from the template process corresponding to + * the ondemand object. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct ondemand { + NCDModuleInst *i; + NCDValRef template_name; + NCDValRef args; + LinkedList1 demands_list; + int dying; + int have_process; + NCDModuleProcess process; + int process_terminating; + int process_up; +}; + +struct demand { + NCDModuleInst *i; + struct ondemand *od; + LinkedList1Node demands_list_node; +}; + +static int ondemand_start_process (struct ondemand *o); +static void ondemand_terminate_process (struct ondemand *o); +static void ondemand_process_handler (NCDModuleProcess *process, int event); +static void ondemand_free (struct ondemand *o); +static void demand_free (struct demand *o, int is_error); + +static int ondemand_start_process (struct ondemand *o) +{ + ASSERT(!o->dying) + ASSERT(!o->have_process) + + // start process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->template_name, o->args, ondemand_process_handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set have process + o->have_process = 1; + + // set process not terminating + o->process_terminating = 0; + + // set process not up + o->process_up = 0; + + return 1; + +fail0: + return 0; +} + +static void ondemand_terminate_process (struct ondemand *o) +{ + ASSERT(o->have_process) + ASSERT(!o->process_terminating) + + // request termination + NCDModuleProcess_Terminate(&o->process); + + // set process terminating + o->process_terminating = 1; + + if (o->process_up) { + // set process down + o->process_up = 0; + + // signal demands down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Down(demand->i); + } + } +} + +static void ondemand_process_handler (NCDModuleProcess *process, int event) +{ + struct ondemand *o = UPPER_OBJECT(process, struct ondemand, process); + ASSERT(o->have_process) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(!o->process_terminating) + ASSERT(!o->process_up) + + // set process up + o->process_up = 1; + + // signal demands up + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Up(demand->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(!o->process_terminating) + ASSERT(o->process_up) + + // continue process + NCDModuleProcess_Continue(&o->process); + + // set process down + o->process_up = 0; + + // signal demands down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Down(demand->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->process_terminating) + ASSERT(!o->process_up) + + // free process + NCDModuleProcess_Free(&o->process); + + // set have no process + o->have_process = 0; + + // if dying, die finally + if (o->dying) { + ondemand_free(o); + return; + } + + // if demands arrivied, restart process + if (!LinkedList1_IsEmpty(&o->demands_list)) { + if (!ondemand_start_process(o)) { + // error demands + while (!LinkedList1_IsEmpty(&o->demands_list)) { + struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node); + ASSERT(demand->od == o) + demand_free(demand, 1); + } + } + } + } break; + } +} + +static void ondemand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ondemand *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_template_name; + NCDValRef arg_args; + if (!NCDVal_ListRead(params->args, 2, &arg_template_name, &arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template_name) || !NCDVal_IsList(arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->template_name = arg_template_name; + o->args = arg_args; + + // init demands list + LinkedList1_Init(&o->demands_list); + + // set not dying + o->dying = 0; + + // set have no process + o->have_process = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ondemand_free (struct ondemand *o) +{ + ASSERT(!o->have_process) + + // die demands + while (!LinkedList1_IsEmpty(&o->demands_list)) { + struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node); + ASSERT(demand->od == o) + demand_free(demand, 0); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void ondemand_func_die (void *vo) +{ + struct ondemand *o = vo; + ASSERT(!o->dying) + + // if not have process, die right away + if (!o->have_process) { + ondemand_free(o); + return; + } + + // set dying + o->dying = 1; + + // request process termination if not already + if (!o->process_terminating) { + ondemand_terminate_process(o); + } +} + +static void demand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct demand *o = vo; + o->i = i; + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set ondemand + o->od = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // add to ondemand's demands list + LinkedList1_Append(&o->od->demands_list, &o->demands_list_node); + + // start process if needed + if (!o->od->have_process) { + ASSERT(!o->od->dying) + + if (!ondemand_start_process(o->od)) { + goto fail1; + } + } + + // if process is up, signal up + if (o->od->process_up) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail1: + LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void demand_free (struct demand *o, int is_error) +{ + // remove from ondemand's demands list + LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node); + + // request process termination if no longer needed + if (o->od->have_process && !o->od->process_terminating && LinkedList1_IsEmpty(&o->od->demands_list)) { + ondemand_terminate_process(o->od); + } + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void demand_func_die (void *vo) +{ + struct demand *o = vo; + + demand_free(o, 0); +} + +static int demand_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct demand *o = vo; + ASSERT(o->od->have_process) + ASSERT(o->od->process_up) + + return NCDModuleProcess_GetObj(&o->od->process, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "ondemand", + .func_new2 = ondemand_func_new, + .func_die = ondemand_func_die, + .alloc_size = sizeof(struct ondemand) + }, { + .type = "ondemand::demand", + .func_new2 = demand_func_new, + .func_die = demand_func_die, + .func_getobj = demand_func_getobj, + .alloc_size = sizeof(struct demand) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_ondemand = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/parse.c b/app/src/main/jni/badvpn/ncd/modules/parse.c new file mode 100644 index 0000000..f4f4065 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/parse.c @@ -0,0 +1,392 @@ +/** + * @file parse.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * parse_number(string str) + * parse_value(string str) + * parse_ipv4_addr(string str) + * parse_ipv6_addr(string str) + * + * Variables: + * succeeded - "true" or "false", reflecting success of the parsing + * (empty) - normalized parsed value (only if succeeded) + * + * Synopsis: + * parse_ipv4_cidr_addr(string str) + * parse_ipv6_cidr_addr(string str) + * + * Variables: + * succeeded - "true" or "false", reflecting success of the parsing + * (empty) - normalized CIDR notation address (only if succeeded) + * addr - normalized address without prefix (only if succeeded) + * prefix - normalized prefix without address (only if succeeded) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef value; + int succeeded; +}; + +struct ipv4_cidr_instance { + NCDModuleInst *i; + int succeeded; + struct ipv4_ifaddr ifaddr; +}; + +struct ipv6_cidr_instance { + NCDModuleInst *i; + int succeeded; + struct ipv6_ifaddr ifaddr; +}; + +enum {STRING_ADDR, STRING_PREFIX}; + +static const char *strings[] = { + "addr", "prefix", NULL +}; + +typedef int (*parse_func) (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out); + +static int parse_number (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + uintmax_t n; + if (!parse_unsigned_integer_bin(str, str_len, &n)) { + ModuleLog(i, BLOG_ERROR, "failed to parse number"); + return 0; + } + + *out = ncd_make_uintmax(mem, n); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static int parse_value (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + if (!NCDValParser_Parse(str, str_len, mem, out)) { + ModuleLog(i, BLOG_ERROR, "failed to parse value"); + return 0; + } + + return 1; +} + +static int parse_ipv4_addr (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(str, str_len, &addr)) { + ModuleLog(i, BLOG_ERROR, "failed to parse ipv4 addresss"); + return 0; + } + + char buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr, buf); + + *out = NCDVal_NewString(mem, buf); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static int parse_ipv6_addr (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + struct ipv6_addr addr; + if (!ipaddr6_parse_ipv6_addr_bin(str, str_len, &addr)) { + ModuleLog(i, BLOG_ERROR, "failed to parse ipv6 addresss"); + return 0; + } + + char buf[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(addr, buf); + + *out = NCDVal_NewString(mem, buf); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static void new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, parse_func pfunc) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // parse + o->succeeded = pfunc(i, NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->mem, &o->value); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (o->succeeded && name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->value); + return 1; + } + + return 0; +} + +static void func_new_parse_number (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_number); +} + +static void func_new_parse_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_value); +} + +static void func_new_parse_ipv4_addr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_ipv4_addr); +} + +static void func_new_parse_ipv6_addr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_ipv6_addr); +} + +static void ipv4_cidr_addr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ipv4_cidr_instance *o = vo; + o->i = i; + + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->ifaddr); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int ipv4_cidr_addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct ipv4_cidr_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (!o->succeeded) { + return 0; + } + + char str[IPADDR_PRINT_MAX]; + + if (name == NCD_STRING_EMPTY) { + ipaddr_print_ifaddr(o->ifaddr, str); + } + else if (name == ModuleString(o->i, STRING_ADDR)) { + ipaddr_print_addr(o->ifaddr.addr, str); + } + else if (name == ModuleString(o->i, STRING_PREFIX)) { + sprintf(str, "%d", o->ifaddr.prefix); + } + else { + return 0; + } + + *out = NCDVal_NewString(mem, str); + return 1; +} + +static void ipv6_cidr_addr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ipv6_cidr_instance *o = vo; + o->i = i; + + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->ifaddr); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int ipv6_cidr_addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct ipv6_cidr_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (!o->succeeded) { + return 0; + } + + char str[IPADDR6_PRINT_MAX]; + + if (name == NCD_STRING_EMPTY) { + ipaddr6_print_ifaddr(o->ifaddr, str); + } + else if (name == ModuleString(o->i, STRING_ADDR)) { + ipaddr6_print_addr(o->ifaddr.addr, str); + } + else if (name == ModuleString(o->i, STRING_PREFIX)) { + sprintf(str, "%d", o->ifaddr.prefix); + } + else { + return 0; + } + + *out = NCDVal_NewString(mem, str); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "parse_number", + .func_new2 = func_new_parse_number, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_value", + .func_new2 = func_new_parse_value, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv4_addr", + .func_new2 = func_new_parse_ipv4_addr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv6_addr", + .func_new2 = func_new_parse_ipv6_addr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv4_cidr_addr", + .func_new2 = ipv4_cidr_addr_func_new, + .func_getvar2 = ipv4_cidr_addr_func_getvar2, + .alloc_size = sizeof(struct ipv4_cidr_instance) + }, { + .type = "parse_ipv6_cidr_addr", + .func_new2 = ipv6_cidr_addr_func_new, + .func_getvar2 = ipv6_cidr_addr_func_getvar2, + .alloc_size = sizeof(struct ipv6_cidr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_parse = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/print.c b/app/src/main/jni/badvpn/ncd/modules/print.c new file mode 100644 index 0000000..6fa0b56 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/print.c @@ -0,0 +1,207 @@ +/** + * @file print.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Modules for printing to standard output. + * + * Synopsis: + * print([string str ...]) + * Description: + * On initialization, prints strings to standard output. + * + * Synopsis: + * println([string str ...]) + * Description: + * On initialization, prints strings to standard output, and a newline. + * + * Synopsis: + * rprint([string str ...]) + * Description: + * On deinitialization, prints strings to standard output. + * + * Synopsis: + * rprintln([string str ...]) + * Description: + * On deinitialization, prints strings to standard output, and a newline. + */ + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct rprint_instance { + NCDModuleInst *i; + NCDValRef args; + int ln; +}; + +static int check_args (NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + size_t num_args = NCDVal_ListCount(params->args); + + for (size_t j = 0; j < num_args; j++) { + NCDValRef arg = NCDVal_ListGet(params->args, j); + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + return 0; + } + } + + return 1; +} + +static void do_print (NCDModuleInst *i, NCDValRef args, int ln) +{ + size_t num_args = NCDVal_ListCount(args); + + for (size_t j = 0; j < num_args; j++) { + NCDValRef arg = NCDVal_ListGet(args, j); + ASSERT(NCDVal_IsString(arg)) + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + + B_CSTRING_LOOP_RANGE(arg_cstr, 0, arg_cstr.length, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + ssize_t res = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, stdout); + if (res <= 0) { + goto out; + } + chunk_pos += res; + } + }) + } + +out: + if (ln) { + printf("\n"); + } +} + +static void rprint_func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int ln) +{ + struct rprint_instance *o = vo; + o->i = i; + o->args = params->args; + o->ln = ln; + + if (!check_args(i, params)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rprint_func_die (void *vo) +{ + struct rprint_instance *o = vo; + + do_print(o->i, o->args, o->ln); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void print_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!check_args(i, params)) { + goto fail0; + } + + do_print(i, params->args, 0); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void println_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!check_args(i, params)) { + goto fail0; + } + + do_print(i, params->args, 1); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rprint_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + return rprint_func_new_common(vo, i, params, 0); +} + +static void rprintln_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + return rprint_func_new_common(vo, i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "print", + .func_new2 = print_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "println", + .func_new2 = println_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "rprint", + .func_new2 = rprint_func_new, + .func_die = rprint_func_die, + .alloc_size = sizeof(struct rprint_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "rprintln", + .func_new2 = rprintln_func_new, + .func_die = rprint_func_die, + .alloc_size = sizeof(struct rprint_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_print = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/process_manager.c b/app/src/main/jni/badvpn/ncd/modules/process_manager.c new file mode 100644 index 0000000..390c8d5 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/process_manager.c @@ -0,0 +1,538 @@ +/** + * @file process_manager.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which allows starting and controlling template processes using an imperative + * interface. + * + * Synopsis: + * process_manager() + * + * Description: + * Represents a set of managed processes. Each process has a "name", which is a value + * that uniquely identifies it within its process manager. + * When deinitialization is requested, requests termination of all managed processes + * and waits for all of them to terminate before deinitializing. + * Managed processes can access objects as seen from the process_manager() statement + * via the special _caller object. + * + * Synopsis: + * process_manager::start(name, string template_name, list args) + * process_manager::start(string template_name, list args) + * + * Description: + * Creates a new process from the template named 'template_name', with arguments 'args', + * identified by 'name' within the process manager. If the two-argument form of start() is + * used, the process does not have a name, and cannot be imperatively stopped using + * stop(). + * If a process with this name already exists and is not being terminated, does nothing. + * If it exists and is being terminated, it will be restarted using the given parameters + * after it terminates. If the process does not exist, it is created immediately, and the + * immediate effects of the process being created happnen before the immediate effects of + * the start() statement going up. + * + * Synopsis: + * process_manager::stop(name) + * + * Description: + * Initiates termination of the process identified by 'name' within the process manager. + * If there is no such process, or the process is already being terminated, does nothing. + * If the process does exist and is not already being terminated, termination of the + * process is requested, and the immediate effects of the termination request happen + * before the immediate effects of the stop() statement going up. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define RETRY_TIME 10000 + +#define PROCESS_STATE_RUNNING 1 +#define PROCESS_STATE_STOPPING 2 +#define PROCESS_STATE_RESTARTING 3 +#define PROCESS_STATE_RETRYING 4 + +struct instance { + NCDModuleInst *i; + LinkedList1 processes_list; + int dying; +}; + +struct process { + struct instance *manager; + LinkedList1Node processes_list_node; + BSmallTimer retry_timer; // running if state=retrying + int state; + NCD_string_id_t template_name; + NCDValMem current_mem; + NCDValSafeRef current_name; + NCDValSafeRef current_args; + NCDValMem next_mem; // next_* if state=restarting + NCDValSafeRef next_name; + NCDValSafeRef next_args; + NCDModuleProcess module_process; // if state!=retrying +}; + +static struct process * find_process (struct instance *o, NCDValRef name); +static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args); +static void process_free (struct process *p); +static void process_try (struct process *p); +static void process_retry_timer_handler (BSmallTimer *retry_timer); +static void process_module_process_handler_event (NCDModuleProcess *module_process, int event); +static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object); +static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void process_stop (struct process *p); +static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args); +static void instance_free (struct instance *o); + +static struct process * find_process (struct instance *o, NCDValRef name) +{ + ASSERT(!NCDVal_IsInvalid(name)) + + LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list); + while (n) { + struct process *p = UPPER_OBJECT(n, struct process, processes_list_node); + ASSERT(p->manager == o) + if (!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) && NCDVal_Compare(NCDVal_FromSafe(&p->current_mem, p->current_name), name) == 0) { + return p; + } + n = LinkedList1Node_Next(n); + } + + return NULL; +} + +static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args) +{ + ASSERT(!o->dying) + ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(mem, name)) || !find_process(o, NCDVal_FromSafe(mem, name))) + ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name))) + ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args))) + + // allocate structure + struct process *p = BAlloc(sizeof(*p)); + if (!p) { + ModuleLog(o->i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + // set manager + p->manager = o; + + // insert to processes list + LinkedList1_Append(&o->processes_list, &p->processes_list_node); + + // init retry timer + BSmallTimer_Init(&p->retry_timer, process_retry_timer_handler); + + // init template name + p->template_name = ncd_get_string_id(NCDVal_FromSafe(mem, template_name), o->i->params->iparams->string_index); + if (p->template_name < 0) { + ModuleLog(o->i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail1; + } + + // init current mem as a copy of mem + if (!NCDValMem_InitCopy(&p->current_mem, mem)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed"); + goto fail1; + } + + // remember name and args + p->current_name = name; + p->current_args = args; + + // try starting it + process_try(p); + return 1; + +fail1: + LinkedList1_Remove(&o->processes_list, &p->processes_list_node); + BFree(p); +fail0: + return 0; +} + +static void process_free (struct process *p) +{ + struct instance *o = p->manager; + + // free current mem + NCDValMem_Free(&p->current_mem); + + // free timer + BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &p->retry_timer); + + // remove from processes list + LinkedList1_Remove(&o->processes_list, &p->processes_list_node); + + // free structure + BFree(p); +} + +static void process_try (struct process *p) +{ + struct instance *o = p->manager; + ASSERT(!o->dying) + ASSERT(!BSmallTimer_IsRunning(&p->retry_timer)) + + // init module process + if (!NCDModuleProcess_InitId(&p->module_process, o->i, p->template_name, NCDVal_FromSafe(&p->current_mem, p->current_args), process_module_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&p->module_process, process_module_process_func_getspecialobj); + + // set state + p->state = PROCESS_STATE_RUNNING; + return; + +fail: + // set timer + BReactor_SetSmallTimer(o->i->params->iparams->reactor, &p->retry_timer, BTIMER_SET_RELATIVE, RETRY_TIME); + + // set state + p->state = PROCESS_STATE_RETRYING; +} + +static void process_retry_timer_handler (BSmallTimer *retry_timer) +{ + struct process *p = UPPER_OBJECT(retry_timer, struct process, retry_timer); + struct instance *o = p->manager; + B_USE(o) + ASSERT(p->state == PROCESS_STATE_RETRYING) + ASSERT(!o->dying) + + // retry + process_try(p); +} + +void process_module_process_handler_event (NCDModuleProcess *module_process, int event) +{ + struct process *p = UPPER_OBJECT(module_process, struct process, module_process); + struct instance *o = p->manager; + ASSERT(p->state != PROCESS_STATE_RETRYING) + ASSERT(p->state != PROCESS_STATE_RESTARTING || !o->dying) + ASSERT(!BSmallTimer_IsRunning(&p->retry_timer)) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(p->state == PROCESS_STATE_RUNNING) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(p->state == PROCESS_STATE_RUNNING) + + // allow process to continue + NCDModuleProcess_Continue(&p->module_process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(p->state == PROCESS_STATE_RESTARTING || p->state == PROCESS_STATE_STOPPING) + + // free module process + NCDModuleProcess_Free(&p->module_process); + + if (p->state == PROCESS_STATE_RESTARTING) { + // free current mem + NCDValMem_Free(&p->current_mem); + + // move next mem/values over current mem/values + p->current_mem = p->next_mem; + p->current_name = p->next_name; + p->current_args = p->next_args; + + // try starting it again + process_try(p); + return; + } + + // free process + process_free(p); + + // if manager is dying and there are no more processes, let it die + if (o->dying && LinkedList1_IsEmpty(&o->processes_list)) { + instance_free(o); + } + } break; + } +} + +static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object) +{ + struct process *p = UPPER_OBJECT(module_process, struct process, module_process); + ASSERT(p->state != PROCESS_STATE_RETRYING) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, p, NCDObject_no_getvar, process_module_process_caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct process *p = NCDObject_DataPtr(obj); + struct instance *o = p->manager; + ASSERT(p->state != PROCESS_STATE_RETRYING) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void process_stop (struct process *p) +{ + switch (p->state) { + case PROCESS_STATE_RETRYING: { + // free process + process_free(p); + } break; + + case PROCESS_STATE_RUNNING: { + // request process to terminate + NCDModuleProcess_Terminate(&p->module_process); + + // set state + p->state = PROCESS_STATE_STOPPING; + } break; + + case PROCESS_STATE_RESTARTING: { + // free next mem + NCDValMem_Free(&p->next_mem); + + // set state + p->state = PROCESS_STATE_STOPPING; + } break; + + case PROCESS_STATE_STOPPING: { + // nothing to do + } break; + + default: ASSERT(0); + } +} + +static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args) +{ + struct instance *o = p->manager; + ASSERT(!o->dying) + ASSERT(p->state == PROCESS_STATE_STOPPING) + ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_IsInvalid(NCDVal_FromSafe(mem, name))) + ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_Compare(NCDVal_FromSafe(mem, name), NCDVal_FromSafe(&p->current_mem, p->current_name)) == 0) + ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name))) + ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args))) + + // copy mem to next mem + if (!NCDValMem_InitCopy(&p->next_mem, mem)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed"); + goto fail0; + } + + // remember name and args to next + p->next_name = name; + p->next_args = args; + + // set state + p->state = PROCESS_STATE_RESTARTING; + return 1; + +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init processes list + LinkedList1_Init(&o->processes_list); + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->processes_list)) + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // request all processes to die + LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list); + while (n) { + LinkedList1Node *next = LinkedList1Node_Next(n); + struct process *p = UPPER_OBJECT(n, struct process, processes_list_node); + process_stop(p); + n = next; + } + + // if there are no processes, die immediately + if (LinkedList1_IsEmpty(&o->processes_list)) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; +} + +static void start_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg = NCDVal_NewInvalid(); + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg) && + !NCDVal_ListRead(params->args, 3, &name_arg, &template_name_arg, &args_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up. + // Do it before creating the process so that the process starts initializing before our own process continues. + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (!mo->dying) { + struct process *p = (NCDVal_IsInvalid(name_arg) ? NULL : find_process(mo, name_arg)); + if (!p || p->state == PROCESS_STATE_STOPPING) { + if (p) { + if (!process_restart(p, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) { + ModuleLog(i, BLOG_ERROR, "failed to restart process"); + goto fail0; + } + } else { + if (!process_new(mo, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) { + ModuleLog(i, BLOG_ERROR, "failed to create process"); + goto fail0; + } + } + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stop_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up. + // Do it before stopping the process so that the process starts terminating before our own process continues. + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (!mo->dying) { + struct process *p = find_process(mo, name_arg); + if (p && p->state != PROCESS_STATE_STOPPING) { + process_stop(p); + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "process_manager", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "process_manager::start", + .func_new2 = start_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "process_manager::stop", + .func_new2 = stop_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_process_manager = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/reboot.c b/app/src/main/jni/badvpn/ncd/modules/reboot.c new file mode 100644 index 0000000..3522431 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/reboot.c @@ -0,0 +1,103 @@ +/** + * @file reboot.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * hard_reboot() + * hard_poweroff() + */ + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new_hard_reboot (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // reboot + if (reboot(RB_AUTOBOOT) < 0) { + ModuleLog(i, BLOG_ERROR, "reboot(RB_AUTOBOOT) failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_hard_poweroff (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // power off + if (reboot(RB_POWER_OFF) < 0) { + ModuleLog(i, BLOG_ERROR, "reboot(RB_POWER_OFF) failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "hard_reboot", + .func_new2 = func_new_hard_reboot + }, { + .type = "hard_poweroff", + .func_new2 = func_new_hard_poweroff + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_reboot = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/ref.c b/app/src/main/jni/badvpn/ncd/modules/ref.c new file mode 100644 index 0000000..a0e9cf8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/ref.c @@ -0,0 +1,215 @@ +/** + * @file ref.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * References module. + * + * Synopsis: + * refhere() + * Variables: + * Exposes variables and objects as seen from this refhere() statement. + * + * Synopsis: + * ref refhere::ref() + * ref ref::ref() + * Variables: + * Exposes variables and objects as seen from the corresponding refhere() + * statement. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct refhere_instance { + NCDModuleInst *i; + LinkedList0 refs_list; +}; + +struct ref_instance { + NCDModuleInst *i; + struct refhere_instance *rh; + LinkedList0Node refs_list_node; +}; + +static void ref_instance_free (struct ref_instance *o); + +static void refhere_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct refhere_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init refs list + LinkedList0_Init(&o->refs_list); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void refhere_func_die (void *vo) +{ + struct refhere_instance *o = vo; + + // die refs + while (!LinkedList0_IsEmpty(&o->refs_list)) { + struct ref_instance *ref = UPPER_OBJECT(LinkedList0_GetFirst(&o->refs_list), struct ref_instance, refs_list_node); + ASSERT(ref->rh == o) + ref_instance_free(ref); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int refhere_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct refhere_instance *o = vo; + + // We don't redirect methods, and there will never be an object + // with empty name. Fail here so we don't report non-errors. + if (objname == NCD_STRING_EMPTY) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->i, objname, out_object); +} + +static void ref_func_new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, struct refhere_instance *rh) +{ + struct ref_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set refhere + o->rh = rh; + + // add to refhere's refs list + LinkedList0_Prepend(&o->rh->refs_list, &o->refs_list_node); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ref_func_new_from_refhere (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct refhere_instance *rh = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + return ref_func_new_templ(vo, i, params, rh); +} + +static void ref_func_new_from_ref (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ref_instance *ref = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + return ref_func_new_templ(vo, i, params, ref->rh); +} + +static void ref_instance_free (struct ref_instance *o) +{ + // remove from refhere's reft list + LinkedList0_Remove(&o->rh->refs_list, &o->refs_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void ref_func_die (void *vo) +{ + struct ref_instance *o = vo; + + ref_instance_free(o); +} + +static int ref_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct ref_instance *o = vo; + + // We don't redirect methods, and there will never be an object + // with empty name. Fail here so we don't report non-errors. + if (objname == NCD_STRING_EMPTY) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->rh->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "refhere", + .func_new2 = refhere_func_new, + .func_die = refhere_func_die, + .func_getobj = refhere_func_getobj, + .alloc_size = sizeof(struct refhere_instance) + }, { + .type = "refhere::ref", + .base_type = "ref", + .func_new2 = ref_func_new_from_refhere, + .func_die = ref_func_die, + .func_getobj = ref_func_getobj, + .alloc_size = sizeof(struct ref_instance) + }, { + .type = "ref::ref", + .base_type = "ref", + .func_new2 = ref_func_new_from_ref, + .func_die = ref_func_die, + .func_getobj = ref_func_getobj, + .alloc_size = sizeof(struct ref_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_ref = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/regex_match.c b/app/src/main/jni/badvpn/ncd/modules/regex_match.c new file mode 100644 index 0000000..d541b88 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/regex_match.c @@ -0,0 +1,369 @@ +/** + * @file regex_match.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Regular expression matching module. + * + * Synopsis: + * regex_match(string input, string regex) + * + * Variables: + * succeeded - "true" or "false", indicating whether input matched regex + * matchN - for N=0,1,2,..., the matching data for the N-th subexpression + * (match0 = whole match) + * + * Description: + * Matches 'input' with the POSIX extended regular expression 'regex'. + * 'regex' must be a string without null bytes, but 'input' can contain null bytes. + * However, it's difficult, if not impossible, to actually match nulls with the regular + * expression. + * The input and regex strings are interpreted according to the POSIX regex functions + * (regcomp(), regexec()); in particular, the current locale setting affects the + * interpretation. + * + * Synopsis: + * regex_replace(string input, list(string) regex, list(string) replace) + * + * Variables: + * string (empty) - transformed input + * + * Description: + * Replaces matching parts of a string. Replacement is performed by repetedly matching + * the remaining part of the string with all regular expressions. On each step, out of + * all regular expressions that match the remainder of the string, the one whose match + * starts at the least position wins, and the matching part is replaced with the + * replacement string corresponding to this regular expression. The process continues + * from the end of the just-replaced portion until no more regular expressions match. + * If multiple regular expressions match at the least position, the one that appears + * first in the 'regex' argument wins. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define MAX_MATCHES 64 + +struct instance { + NCDModuleInst *i; + const char *input; + size_t input_len; + int succeeded; + int num_matches; + regmatch_t matches[MAX_MATCHES]; +}; + +struct replace_instance { + NCDModuleInst *i; + char *output; + size_t output_len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef input_arg; + NCDValRef regex_arg; + if (!NCDVal_ListRead(params->args, 2, &input_arg, ®ex_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(input_arg) || !NCDVal_IsStringNoNulls(regex_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->input = NCDVal_StringData(input_arg); + o->input_len = NCDVal_StringLength(input_arg); + + // make sure we don't overflow regoff_t + if (o->input_len > INT_MAX) { + ModuleLog(o->i, BLOG_ERROR, "input string too long"); + goto fail0; + } + + // null terminate regex + NCDValNullTermString regex_nts; + if (!NCDVal_StringNullTerminate(regex_arg, ®ex_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // compile regex + regex_t preg; + int ret = regcomp(&preg, regex_nts.data, REG_EXTENDED); + NCDValNullTermString_Free(®ex_nts); + if (ret != 0) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed (error=%d)", ret); + goto fail0; + } + + // execute match + o->matches[0].rm_so = 0; + o->matches[0].rm_eo = o->input_len; + o->succeeded = (regexec(&preg, o->input, MAX_MATCHES, o->matches, REG_STARTEND) == 0); + + // free regex + regfree(&preg); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "succeeded")) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + size_t pos; + uintmax_t n; + if ((pos = string_begins_with(name, "match")) && parse_unsigned_integer(name + pos, &n)) { + if (o->succeeded && n < MAX_MATCHES && o->matches[n].rm_so >= 0) { + regmatch_t *m = &o->matches[n]; + + ASSERT(m->rm_so <= o->input_len) + ASSERT(m->rm_eo >= m->rm_so) + ASSERT(m->rm_eo <= o->input_len) + + size_t len = m->rm_eo - m->rm_so; + + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->input + m->rm_so, len); + return 1; + } + } + + return 0; +} + +static void replace_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct replace_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef input_arg; + NCDValRef regex_arg; + NCDValRef replace_arg; + if (!NCDVal_ListRead(params->args, 3, &input_arg, ®ex_arg, &replace_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsString(input_arg) || !NCDVal_IsList(regex_arg) || !NCDVal_IsList(replace_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // check number of regex/replace + if (NCDVal_ListCount(regex_arg) != NCDVal_ListCount(replace_arg)) { + ModuleLog(i, BLOG_ERROR, "number of regex's is not the same as number of replacements"); + goto fail1; + } + size_t num_regex = NCDVal_ListCount(regex_arg); + + // allocate array for compiled regex's + regex_t *regs = BAllocArray(num_regex, sizeof(regs[0])); + if (!regs) { + ModuleLog(i, BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + size_t num_done_regex = 0; + + // compile regex's, check arguments + while (num_done_regex < num_regex) { + NCDValRef regex = NCDVal_ListGet(regex_arg, num_done_regex); + NCDValRef replace = NCDVal_ListGet(replace_arg, num_done_regex); + + if (!NCDVal_IsStringNoNulls(regex) || !NCDVal_IsString(replace)) { + ModuleLog(i, BLOG_ERROR, "wrong regex/replace type for pair %zu", num_done_regex); + goto fail2; + } + + // null terminate regex + NCDValNullTermString regex_nts; + if (!NCDVal_StringNullTerminate(regex, ®ex_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail2; + } + + int res = regcomp(®s[num_done_regex], regex_nts.data, REG_EXTENDED); + NCDValNullTermString_Free(®ex_nts); + if (res != 0) { + ModuleLog(i, BLOG_ERROR, "regcomp failed for pair %zu (error=%d)", num_done_regex, res); + goto fail2; + } + + num_done_regex++; + } + + // init output string + ExpString out; + if (!ExpString_Init(&out)) { + ModuleLog(i, BLOG_ERROR, "ExpString_Init failed"); + goto fail2; + } + + // input state + const char *in = NCDVal_StringData(input_arg); + size_t in_pos = 0; + size_t in_len = NCDVal_StringLength(input_arg); + + // process input + while (in_pos < in_len) { + // find first match + int have_match = 0; + size_t match_regex = 0; // to remove warning + regmatch_t match = {0, 0}; // to remove warning + for (size_t j = 0; j < num_regex; j++) { + regmatch_t this_match; + this_match.rm_so = 0; + this_match.rm_eo = in_len - in_pos; + if (regexec(®s[j], in + in_pos, 1, &this_match, REG_STARTEND) == 0 && (!have_match || this_match.rm_so < match.rm_so)) { + have_match = 1; + match_regex = j; + match = this_match; + } + } + + // if no match, append remaining data and finish + if (!have_match) { + if (!ExpString_AppendBinary(&out, (const uint8_t *)in + in_pos, in_len - in_pos)) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + break; + } + + // append data before match + if (!ExpString_AppendBinary(&out, (const uint8_t *)in + in_pos, match.rm_so)) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + + // append replacement data + NCDValRef replace = NCDVal_ListGet(replace_arg, match_regex); + if (!ExpString_AppendBinary(&out, (const uint8_t *)NCDVal_StringData(replace), NCDVal_StringLength(replace))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + + in_pos += match.rm_eo; + } + + // set output + o->output = ExpString_Get(&out); + o->output_len = ExpString_Length(&out); + + // free compiled regex's + while (num_done_regex-- > 0) { + regfree(®s[num_done_regex]); + } + + // free array + BFree(regs); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail3: + ExpString_Free(&out); +fail2: + while (num_done_regex-- > 0) { + regfree(®s[num_done_regex]); + } + BFree(regs); +fail1: + NCDModuleInst_Backend_DeadError(i); +} + +static void replace_func_die (void *vo) +{ + struct replace_instance *o = vo; + + // free output + BFree(o->output); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int replace_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct replace_instance *o = vo; + + if (!strcmp(name, "")) { + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->output, o->output_len); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "regex_match", + .func_new2 = func_new, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "regex_replace", + .func_new2 = replace_func_new, + .func_die = replace_func_die, + .func_getvar = replace_func_getvar, + .alloc_size = sizeof(struct replace_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_regex_match = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/run.c b/app/src/main/jni/badvpn/ncd/modules/run.c new file mode 100644 index 0000000..147914c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/run.c @@ -0,0 +1,187 @@ +/** + * @file run.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for running arbitrary programs. + * NOTE: There is no locking - the program may run in parallel with other + * NCD processes and their programs. + * + * Synopsis: run(list do_cmd, list undo_cmd) + * Arguments: + * list do_cmd - Command run on startup. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). An empty list is interpreted as no operation. + * list undo_cmd - Command run on shutdown, like do_cmd. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void template_free_func (void *vo, int is_error); + +struct instance { + NCDModuleInst *i; + BEventLock lock; + command_template_instance cti; +}; + +static int build_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef do_cmd_arg; + NCDValRef undo_cmd_arg; + if (!NCDVal_ListRead(args, 2, &do_cmd_arg, &undo_cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(do_cmd_arg) || !NCDVal_IsList(undo_cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValRef list = (remove ? undo_cmd_arg : do_cmd_arg); + size_t count = NCDVal_ListCount(list); + + // check if there is no command + if (count == 0) { + *exec = NULL; + return 1; + } + + // read exec + NCDValRef exec_arg = NCDVal_ListGet(list, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, arg_cstr, 0, arg_cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init dummy event lock + BEventLock_Init(&o->lock, BReactor_PendingGroup(i->params->iparams->reactor)); + + command_template_new(&o->cti, i, params, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &o->lock); + return; +} + +void template_free_func (void *vo, int is_error) +{ + struct instance *o = vo; + + // free dummy event lock + BEventLock_Free(&o->lock); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + command_template_die(&o->cti); +} + +static struct NCDModule modules[] = { + { + .type = "run", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_run= { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/runonce.c b/app/src/main/jni/badvpn/ncd/modules/runonce.c new file mode 100644 index 0000000..bd7cea4 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/runonce.c @@ -0,0 +1,331 @@ +/** + * @file runonce.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Imperative program execution module. On initialization, starts the process. + * Goes to UP state when the process terminates. When requested to die, waits for + * the process to terminate if it's running, optionally sending SIGTERM. + * + * Synopsis: runonce(list(string) cmd, [list opts]) + * Arguments: + * cmd - Command to run on startup. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). + * opts - Map of options: + * "term_on_deinit":"true" - If we get a deinit request while the process is + * running, send it SIGTERM. + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + * Variables: + * string exit_status - if the program exited normally, the non-negative exit code, otherwise -1 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_RUNNING 1 +#define STATE_RUNNING_DIE 2 +#define STATE_FINISHED 3 + +struct instance { + NCDModuleInst *i; + int term_on_deinit; + int state; + BProcess process; + int exit_status; +}; + +static void instance_free (struct instance *o); + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + + if (!NCDVal_IsList(cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + ModuleLog(i, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, arg_cstr, 0, arg_cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_RUNNING || o->state == STATE_RUNNING_DIE) + + // free process + BProcess_Free(&o->process); + + // if we were requested to die, die now + if (o->state == STATE_RUNNING_DIE) { + instance_free(o); + return; + } + + // remember exit status + o->exit_status = (normally ? normally_exit_status : -1); + + // set state + o->state = STATE_FINISHED; + + // set up + NCDModuleInst_Backend_Up(o->i); +} + +static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val) +{ + struct instance *o = user; + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) { + o->term_on_deinit = ncd_read_boolean(val); + return 1; + } + + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init arguments + o->term_on_deinit = 0; + + // read arguments + NCDValRef cmd_arg; + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &cmd_arg) && !NCDVal_ListRead(params->args, 2, &cmd_arg, &opts_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + NCDBProcessOpts opts; + + // deprecated options format + if (!NCDVal_IsInvalid(opts_arg) && NCDVal_IsList(opts_arg)) { + int keep_stdout = 0; + int keep_stderr = 0; + int do_setsid = 0; + + // read options + size_t count = NCDVal_IsInvalid(opts_arg) ? 0 : NCDVal_ListCount(opts_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef opt = NCDVal_ListGet(opts_arg, j); + + // read name + if (!NCDVal_IsString(opt)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option name type"); + goto fail0; + } + + if (NCDVal_StringEquals(opt, "term_on_deinit")) { + o->term_on_deinit = 1; + } + else if (NCDVal_StringEquals(opt, "keep_stdout")) { + keep_stdout = 1; + } + else if (NCDVal_StringEquals(opt, "keep_stderr")) { + keep_stderr = 1; + } + else if (NCDVal_StringEquals(opt, "do_setsid")) { + do_setsid = 1; + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown option name"); + goto fail0; + } + } + + NCDBProcessOpts_InitOld(&opts, keep_stdout, keep_stderr, do_setsid); + } else { + if (!NCDBProcessOpts_Init(&opts, opts_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL)) { + goto fail0; + } + } + + // build cmdline + char *exec; + CmdLine cl; + if (!build_cmdline(o->i, cmd_arg, &exec, &cl)) { + NCDBProcessOpts_Free(&opts); + goto fail0; + } + + // start process + struct BProcess_params p_params = NCDBProcessOpts_GetParams(&opts); + if (!BProcess_Init2(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, exec, CmdLine_Get(&cl), p_params)) { + ModuleLog(i, BLOG_ERROR, "BProcess_Init failed"); + CmdLine_Free(&cl); + free(exec); + NCDBProcessOpts_Free(&opts); + goto fail0; + } + + CmdLine_Free(&cl); + free(exec); + NCDBProcessOpts_Free(&opts); + + // set state + o->state = STATE_RUNNING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_RUNNING_DIE) + + if (o->state == STATE_FINISHED) { + instance_free(o); + return; + } + + // send SIGTERM if requested + if (o->term_on_deinit) { + BProcess_Terminate(&o->process); + } + + o->state = STATE_RUNNING_DIE; +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_FINISHED) + + if (!strcmp(name, "exit_status")) { + char str[30]; + snprintf(str, sizeof(str), "%d", o->exit_status); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "runonce", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_runonce = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sleep.c b/app/src/main/jni/badvpn/ncd/modules/sleep.c new file mode 100644 index 0000000..a139aa9 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sleep.c @@ -0,0 +1,178 @@ +/** + * @file sleep.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sleep(string ms_start [, string ms_stop]) + * + * Description: + * On init, sleeps 'ms_start' milliseconds then goes up, or goes up immediately + * if 'ms_start' is an empty string. + * On deinit, sleeps 'ms_stop' milliseconds then dies, or dies immediately if + * 'ms_stop' is an empty string or is not provided. If a deinit is requested while + * the init sleep is still in progress, the init sleep is aborted and the deinit + * sleep is started immediately (if any). + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + btime_t ms_stop; + BTimer timer; + int dying; +}; + +static void instance_free (struct instance *o); + +static void timer_handler (void *vo) +{ + struct instance *o = vo; + + if (!o->dying) { + // signal up + NCDModuleInst_Backend_Up(o->i); + } else { + // die + instance_free(o); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ms_start_arg; + NCDValRef ms_stop_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &ms_start_arg) && + !NCDVal_ListRead(params->args, 2, &ms_start_arg, &ms_stop_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(ms_start_arg) || (!NCDVal_IsInvalid(ms_stop_arg) && !NCDVal_IsString(ms_stop_arg))) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t ms; + btime_t ms_start; + + if (NCDVal_StringEqualsId(ms_start_arg, NCD_STRING_EMPTY, i->params->iparams->string_index)) { + ms_start = -1; + } else { + if (!ncd_read_uintmax(ms_start_arg, &ms) || ms > INT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong start time"); + goto fail0; + } + ms_start = ms; + } + + if (NCDVal_IsInvalid(ms_stop_arg) || NCDVal_StringEqualsId(ms_stop_arg, NCD_STRING_EMPTY, i->params->iparams->string_index)) { + o->ms_stop = -1; + } else { + if (!ncd_read_uintmax(ms_stop_arg, &ms) || ms > INT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong stop time"); + goto fail0; + } + o->ms_stop = ms; + } + + // init timer + BTimer_Init(&o->timer, 0, timer_handler, o); + + // set not dying + o->dying = 0; + + if (ms_start < 0) { + // go up + NCDModuleInst_Backend_Up(i); + } else { + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, ms_start); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + if (o->ms_stop < 0) { + // die immediately + instance_free(o); + return; + } + + // set dying + o->dying = 1; + + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->ms_stop); +} + +static struct NCDModule modules[] = { + { + .type = "sleep", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sleep = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/socket.c b/app/src/main/jni/badvpn/ncd/modules/socket.c new file mode 100644 index 0000000..59ca8d7 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/socket.c @@ -0,0 +1,1057 @@ +/** + * @file socket.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.socket sys.connect(string addr [, map options]) + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Variables: + * string is_error - "true" if there was an error with the connection, + * "false" if not + * + * Description: + * Attempts to establish a connection to a server. The address should be + * in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * When the connection attempt is finished, the sys.connect() statement goes + * up, and the 'is_error' variable should be used to check for connection + * failure. If there was no error, the read(), write() and close() methods + * can be used to work with the connection. + * If an error occurs after the connection has been established, the + * sys.connect() statement will automatically trigger backtracking, and the + * 'is_error' variable will be changed to "true". This means that all + * errors with the connection can be handled at the place of sys.connect(), + * and no special care is normally needed to handle error in read() and + * write(). + * WARNING: when you're not trying to either send or receive data, the + * connection may be unable to detect any events with the connection. + * You should never be neither sending nor receiving for an indefinite time. + * + * Synopsis: + * sys.socket::read() + * + * Variables: + * string (empty) - some data received from the socket, or empty on EOF + * string not_eof - "true" if EOF was not encountered, "false" if it was + * + * Description: + * Receives data from the connection. If EOF was encountered (remote host + * has closed the connection), this returns no data. Otherwise it returns + * at least one byte. + * WARNING: after you receive EOF from a sys.listen() type socket, is is + * your responsibility to call close() eventually, else the cline process + * may remain alive indefinitely. + * WARNING: this may return an arbitrarily small chunk of data. There is + * no significance to the size of the chunks. Correct code will behave + * the same no matter how the incoming data stream is split up. + * WARNING: if a read() is terminated while it is still in progress, i.e. + * has not gone up yet, then the connection is automatically closed, as + * if close() was called. + * + * Synopsis: + * sys.socket::write(string data) + * + * Description: + * Sends data to the connection. + * WARNING: this may block if the operating system's internal send buffer + * is full. Be careful not to enter a deadlock where both ends of the + * connection are trying to send data to the other, but neither is trying + * to receive any data. + * WARNING: if a write() is terminated while it is still in progress, i.e. + * has not gone up yet, then the connection is automatically closed, as + * if close() was called. + * + * Synopsis: + * sys.socket::close() + * + * Description: + * Closes the connection. After this, any further read(), write() or close() + * will trigger an error with the interpreter. For client sockets created + * via sys.listen(), this will immediately trigger termination of the client + * process. + * + * Synopsis: + * sys.listen(string address, string client_template, list args [, map options]) + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Variables: + * string is_error - "true" if listening failed to inittialize, "false" if + * not + * + * Special objects and variables in client_template: + * sys.socket _socket - the socket object for the client + * string _socket.client_addr - the address of the client. The form is + * like the second part of the sys.connect() address format, e.g. + * {"ipv4", "1.2.3.4", "4000"}. + * + * Description: + * Starts listening on the specified address. The 'is_error' variable + * reflects the success of listening initiation. If listening succeeds, + * then for every client that connects, a process is automatically created + * from the template specified by 'client_template', and the 'args' list + * is used as template arguments. Inside such processes, a special object + * '_socket' is available, which represents the connection, and supports + * the same methods as sys.connect(), i.e. read(), write() and close(). + * When an error occurs with the connection, the socket is automatically + * closed, triggering process termination. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define CONNECTION_TYPE_CONNECT 1 +#define CONNECTION_TYPE_LISTEN 2 + +#define CONNECTION_STATE_CONNECTING 1 +#define CONNECTION_STATE_ESTABLISHED 2 +#define CONNECTION_STATE_ERROR 3 +#define CONNECTION_STATE_ABORTED 4 + +#define DEFAULT_READ_BUF_SIZE 8192 + +struct connection { + union { + struct { + NCDModuleInst *i; + BConnector connector; + size_t read_buf_size; + } connect; + struct { + struct listen_instance *listen_inst; + LinkedList0Node clients_list_node; + BAddr addr; + NCDModuleProcess process; + } listen; + }; + + unsigned int type:2; + unsigned int state:3; + unsigned int recv_closed:1; + BConnection connection; + NCDBufStore store; + struct read_instance *read_inst; + struct write_instance *write_inst; +}; + +struct read_instance { + NCDModuleInst *i; + struct connection *con_inst; + NCDBuf *buf; + size_t read_size; +}; + +struct write_instance { + NCDModuleInst *i; + struct connection *con_inst; + b_cstring cstr; + size_t pos; +}; + +struct listen_instance { + NCDModuleInst *i; + unsigned int have_error:1; + unsigned int dying:1; + size_t read_buf_size; + NCDValRef client_template; + NCDValRef client_template_args; + BListener listener; + LinkedList0 clients_list; +}; + +enum {STRING_SOCKET, STRING_SYS_SOCKET, STRING_CLIENT_ADDR}; + +static const char *strings[] = { + "_socket", "sys.socket", "client_addr", NULL +}; + +static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size); +static void connection_log (struct connection *o, int level, const char *fmt, ...); +static void connection_free_connection (struct connection *o); +static void connection_error (struct connection *o); +static void connection_abort (struct connection *o); +static void connection_connector_handler (void *user, int is_error); +static void connection_connection_handler (void *user, int event); +static void connection_send_handler_done (void *user, int data_len); +static void connection_recv_handler_done (void *user, int data_len); +static void connection_process_handler (struct NCDModuleProcess_s *process, int event); +static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object); +static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static void listen_listener_handler (void *user); + +static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size) +{ + ASSERT(out_read_size) + + *out_read_size = DEFAULT_READ_BUF_SIZE; + + if (!NCDVal_IsInvalid(options)) { + if (!NCDVal_IsMap(options)) { + ModuleLog(i, BLOG_ERROR, "options argument is not a map"); + return 0; + } + + int num_recognized = 0; + NCDValRef value; + + if (!NCDVal_IsInvalid(value = NCDVal_MapGetValue(options, "read_size"))) { + uintmax_t read_size; + if (!NCDVal_IsString(value) || !ncd_read_uintmax(value, &read_size) || read_size > SIZE_MAX || read_size == 0) { + ModuleLog(i, BLOG_ERROR, "wrong read_size"); + return 0; + } + num_recognized++; + *out_read_size = read_size; + } + + if (NCDVal_MapCount(options) > num_recognized) { + ModuleLog(i, BLOG_ERROR, "unrecognized options present"); + return 0; + } + } + + return 1; +} + +static void connection_log (struct connection *o, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + switch (o->type) { + case CONNECTION_TYPE_CONNECT: { + NCDModuleInst_Backend_LogVarArg(o->connect.i, BLOG_CURRENT_CHANNEL, level, fmt, vl); + } break; + + case CONNECTION_TYPE_LISTEN: { + if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, level)) { + BLog_Begin(); + o->listen.listen_inst->i->params->logfunc(o->listen.listen_inst->i); + char addr_str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&o->listen.addr, addr_str); + BLog_Append("client %s: ", addr_str); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(BLOG_CURRENT_CHANNEL, level); + } + } break; + + default: ASSERT(0); + } + + va_end(vl); +} + +static void connection_free_connection (struct connection *o) +{ + // disconnect read instance + if (o->read_inst) { + ASSERT(o->read_inst->con_inst == o) + o->read_inst->con_inst = NULL; + } + + // disconnect write instance + if (o->write_inst) { + ASSERT(o->write_inst->con_inst == o) + o->write_inst->con_inst = NULL; + } + + // free connection interfaces + BConnection_RecvAsync_Free(&o->connection); + BConnection_SendAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // free store + NCDBufStore_Free(&o->store); +} + +static void connection_error (struct connection *o) +{ + ASSERT(o->state == CONNECTION_STATE_CONNECTING || + o->state == CONNECTION_STATE_ESTABLISHED) + + // for listen clients, we don't report errors directly, + // we just terminate the client process + if (o->type == CONNECTION_TYPE_LISTEN) { + ASSERT(o->state != CONNECTION_STATE_CONNECTING) + connection_abort(o); + return; + } + + // free connector + if (o->state == CONNECTION_STATE_CONNECTING) { + BConnector_Free(&o->connect.connector); + } + + // free connection resources + if (o->state == CONNECTION_STATE_ESTABLISHED) { + connection_free_connection(o); + } + + // trigger reporting of failure + if (o->state == CONNECTION_STATE_ESTABLISHED) { + NCDModuleInst_Backend_Down(o->connect.i); + } + NCDModuleInst_Backend_Up(o->connect.i); + + // set state + o->state = CONNECTION_STATE_ERROR; +} + +static void connection_abort (struct connection *o) +{ + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + + // free connection resources + connection_free_connection(o); + + // if this is a listen connection, terminate client process + if (o->type == CONNECTION_TYPE_LISTEN) { + NCDModuleProcess_Terminate(&o->listen.process); + } + + // set state + o->state = CONNECTION_STATE_ABORTED; +} + +static void connection_connector_handler (void *user, int is_error) +{ + struct connection *o = user; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + ASSERT(o->state == CONNECTION_STATE_CONNECTING) + + // check error + if (is_error) { + connection_log(o, BLOG_ERROR, "connection failed"); + goto fail; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_connector(&o->connect.connector), o->connect.i->params->iparams->reactor, o, connection_connection_handler)) { + connection_log(o, BLOG_ERROR, "BConnection_Init failed"); + goto fail; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->connection); + BConnection_RecvAsync_Init(&o->connection); + + // setup send/recv done callbacks + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), connection_send_handler_done, o); + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), connection_recv_handler_done, o); + + // init store + NCDBufStore_Init(&o->store, o->connect.read_buf_size); + + // set not reading, not writing, recv not closed + o->read_inst = NULL; + o->write_inst = NULL; + o->recv_closed = 0; + + // free connector + BConnector_Free(&o->connect.connector); + + // set state + o->state = CONNECTION_STATE_ESTABLISHED; + + // go up + NCDModuleInst_Backend_Up(o->connect.i); + return; + +fail: + connection_error(o); +} + +static void connection_connection_handler (void *user, int event) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(event == BCONNECTION_EVENT_RECVCLOSED || event == BCONNECTION_EVENT_ERROR) + ASSERT(event != BCONNECTION_EVENT_RECVCLOSED || !o->recv_closed) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + // if we have read operation, make it finish with eof + if (o->read_inst) { + ASSERT(o->read_inst->con_inst == o) + o->read_inst->con_inst = NULL; + o->read_inst->read_size = 0; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; + } + + // set recv closed + o->recv_closed = 1; + return; + } + + connection_log(o, BLOG_ERROR, "connection error"); + + // handle error + connection_error(o); +} + +static void connection_send_handler_done (void *user, int data_len) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->write_inst) + ASSERT(o->write_inst->con_inst == o) + ASSERT(o->write_inst->pos < o->write_inst->cstr.length) + ASSERT(data_len > 0) + ASSERT(data_len <= o->write_inst->cstr.length - o->write_inst->pos) + + struct write_instance *wr = o->write_inst; + + // update send state + wr->pos += data_len; + + // if there's more to send, send again + if (wr->pos < wr->cstr.length) { + size_t chunk_len; + const char *chunk_data = b_cstring_get(wr->cstr, wr->pos, wr->cstr.length - wr->pos, &chunk_len); + size_t to_send = (chunk_len > INT_MAX ? INT_MAX : chunk_len); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)chunk_data, to_send); + return; + } + + // finish write operation + wr->con_inst = NULL; + NCDModuleInst_Backend_Up(wr->i); + o->write_inst = NULL; +} + +static void connection_recv_handler_done (void *user, int data_len) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->read_inst) + ASSERT(o->read_inst->con_inst == o) + ASSERT(!o->recv_closed) + ASSERT(data_len > 0) + ASSERT(data_len <= NCDBufStore_BufSize(&o->store)) + + struct read_instance *re = o->read_inst; + + // finish read operation + re->con_inst = NULL; + re->read_size = data_len; + NCDModuleInst_Backend_Up(re->i); + o->read_inst = NULL; +} + +static void connection_process_handler (struct NCDModuleProcess_s *process, int event) +{ + struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + NCDModuleProcess_Continue(&o->listen.process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == CONNECTION_STATE_ABORTED) + + struct listen_instance *li = o->listen.listen_inst; + ASSERT(!li->have_error) + + // remove from clients list + LinkedList0_Remove(&li->clients_list, &o->listen.clients_list_node); + + // free process + NCDModuleProcess_Free(&o->listen.process); + + // free connection structure + free(o); + + // if listener is dying and this was the last process, have it die + if (li->dying && LinkedList0_IsEmpty(&li->clients_list)) { + NCDModuleInst_Backend_Dead(li->i); + } + } break; + + default: ASSERT(0); + } +} + +static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + if (name == ModuleString(o->listen.listen_inst->i, STRING_SOCKET)) { + *out_object = NCDObject_Build(ModuleString(o->listen.listen_inst->i, STRING_SYS_SOCKET), o, connection_process_socket_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + struct connection *o = NCDObject_DataPtr(obj); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + if (name == ModuleString(o->listen.listen_inst->i, STRING_CLIENT_ADDR)) { + *out_value = ncd_make_baddr(o->listen.addr, mem); + if (NCDVal_IsInvalid(*out_value)) { + connection_log(o, BLOG_ERROR, "ncd_make_baddr failed"); + } + return 1; + } + + return 0; +} + +static void listen_listener_handler (void *user) +{ + struct listen_instance *o = user; + ASSERT(!o->have_error) + ASSERT(!o->dying) + + // allocate connection structure + struct connection *con = malloc(sizeof(*con)); + if (!con) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set connection type and listen instance + con->type = CONNECTION_TYPE_LISTEN; + con->listen.listen_inst = o; + + // init connection + if (!BConnection_Init(&con->connection, BConnection_source_listener(&o->listener, &con->listen.addr), o->i->params->iparams->reactor, con, connection_connection_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init connection interfaces + BConnection_SendAsync_Init(&con->connection); + BConnection_RecvAsync_Init(&con->connection); + + // setup send/recv done callbacks + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&con->connection), connection_send_handler_done, con); + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&con->connection), connection_recv_handler_done, con); + + // init process + if (!NCDModuleProcess_InitValue(&con->listen.process, o->i, o->client_template, o->client_template_args, connection_process_handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_InitValue failed"); + goto fail2; + } + + // set special objects callback + NCDModuleProcess_SetSpecialFuncs(&con->listen.process, connection_process_func_getspecialobj); + + // insert to clients list + LinkedList0_Prepend(&o->clients_list, &con->listen.clients_list_node); + + // init store + NCDBufStore_Init(&con->store, o->read_buf_size); + + // set not reading, not writing, recv not closed + con->read_inst = NULL; + con->write_inst = NULL; + con->recv_closed = 0; + + // set state + con->state = CONNECTION_STATE_ESTABLISHED; + return; + +fail2: + BConnection_RecvAsync_Free(&con->connection); + BConnection_SendAsync_Free(&con->connection); + BConnection_Free(&con->connection); +fail1: + free(con); +fail0: + return; +} + +static void connect_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct connection *o = vo; + o->type = CONNECTION_TYPE_CONNECT; + o->connect.i = i; + + // pass connection pointer to methods so the same methods can work for + // listen type connections + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + NCDValRef address_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &address_arg) && + !NCDVal_ListRead(params->args, 2, &address_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // parse options + if (!parse_options(i, options_arg, &o->connect.read_buf_size)) { + goto fail0; + } + + // read address + struct BConnection_addr address; + if (!ncd_read_bconnection_addr(address_arg, &address)) { + ModuleLog(i, BLOG_ERROR, "wrong address"); + goto error; + } + + // init connector + if (!BConnector_InitGeneric(&o->connect.connector, address, i->params->iparams->reactor, o, connection_connector_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnector_InitGeneric failed"); + goto error; + } + + // set state + o->state = CONNECTION_STATE_CONNECTING; + return; + +error: + // go up in error state + o->state = CONNECTION_STATE_ERROR; + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void connect_func_die (void *vo) +{ + struct connection *o = vo; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + + // free connector + if (o->state == CONNECTION_STATE_CONNECTING) { + BConnector_Free(&o->connect.connector); + } + + // free connection resources + if (o->state == CONNECTION_STATE_ESTABLISHED) { + connection_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->connect.i); +} + +static int connect_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct connection *o = vo; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + ASSERT(o->state != CONNECTION_STATE_CONNECTING) + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == CONNECTION_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->connect.i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // check if there's already a read in progress + if (con_inst->read_inst) { + ModuleLog(i, BLOG_ERROR, "read is already in progress"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&con_inst->store); + if (!o->buf) { + ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // if eof was reached, go up immediately + if (con_inst->recv_closed) { + o->con_inst = NULL; + o->read_size = 0; + NCDModuleInst_Backend_Up(i); + return; + } + + // set connection + o->con_inst = con_inst; + + // register read operation in connection + con_inst->read_inst = o; + + // receive + size_t buf_size = NCDBufStore_BufSize(&con_inst->store); + int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size); + StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&con_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // if we're receiving, abort connection + if (o->con_inst) { + ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->con_inst->read_inst == o) + connection_abort(o->con_inst); + } + + // release buffer + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + ASSERT(!o->con_inst) + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf)); + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + *out = ncd_make_boolean(mem, (o->read_size != 0), o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // check if there's already a write in progress + if (con_inst->write_inst) { + ModuleLog(i, BLOG_ERROR, "write is already in progress"); + goto fail0; + } + + // set send state + o->cstr = NCDVal_StringCstring(data_arg); + o->pos = 0; + + // if there's nothing to send, go up immediately + if (o->cstr.length == 0) { + o->con_inst = NULL; + NCDModuleInst_Backend_Up(i); + return; + } + + // set connection + o->con_inst = con_inst; + + // register write operation in connection + con_inst->write_inst = o; + + // send + size_t chunk_len; + const char *chunk_data = b_cstring_get(o->cstr, o->pos, o->cstr.length - o->pos, &chunk_len); + size_t to_send = (chunk_len > INT_MAX ? INT_MAX : chunk_len); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&con_inst->connection), (uint8_t *)chunk_data, to_send); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_func_die (void *vo) +{ + struct write_instance *o = vo; + + // if we're sending, abort connection + if (o->con_inst) { + ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->con_inst->write_inst == o) + connection_abort(o->con_inst); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // abort + connection_abort(con_inst); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void listen_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct listen_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef address_arg; + NCDValRef client_template_arg; + NCDValRef args_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 3, &address_arg, &client_template_arg, &args_arg) && + !NCDVal_ListRead(params->args, 4, &address_arg, &client_template_arg, &args_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(client_template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse options + if (!parse_options(i, options_arg, &o->read_buf_size)) { + goto fail0; + } + + // remember client template and arguments + o->client_template = client_template_arg; + o->client_template_args = args_arg; + + // set no error, not dying + o->have_error = 0; + o->dying = 0; + + // read address + struct BConnection_addr address; + if (!ncd_read_bconnection_addr(address_arg, &address)) { + ModuleLog(i, BLOG_ERROR, "wrong address"); + goto error; + } + + // init listener + if (!BListener_InitGeneric(&o->listener, address, i->params->iparams->reactor, o, listen_listener_handler)) { + ModuleLog(i, BLOG_ERROR, "BListener_InitGeneric failed"); + goto error; + } + + // init clients list + LinkedList0_Init(&o->clients_list); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +error: + // go up with error + o->have_error = 1; + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void listen_func_die (void *vo) +{ + struct listen_instance *o = vo; + ASSERT(!o->dying) + + // free listener + if (!o->have_error) { + BListener_Free(&o->listener); + } + + // if we have no clients, die right away + if (o->have_error || LinkedList0_IsEmpty(&o->clients_list)) { + NCDModuleInst_Backend_Dead(o->i); + return; + } + + // set dying + o->dying = 1; + + // abort all clients and wait for them + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clients_list); ln; ln = LinkedList0Node_Next(ln)) { + struct connection *con = UPPER_OBJECT(ln, struct connection, listen.clients_list_node); + ASSERT(con->type == CONNECTION_TYPE_LISTEN) + ASSERT(con->listen.listen_inst == o) + ASSERT(con->state == CONNECTION_STATE_ESTABLISHED || con->state == CONNECTION_STATE_ABORTED) + + if (con->state != CONNECTION_STATE_ABORTED) { + connection_abort(con); + } + } +} + +static int listen_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct listen_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + *out = ncd_make_boolean(mem, o->have_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "sys.connect", + .base_type = "sys.socket", + .func_new2 = connect_func_new, + .func_die = connect_func_die, + .func_getvar2 = connect_func_getvar, + .alloc_size = sizeof(struct connection), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::write", + .func_new2 = write_func_new, + .func_die = write_func_die, + .alloc_size = sizeof(struct write_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.listen", + .func_new2 = listen_func_new, + .func_die = listen_func_die, + .func_getvar2 = listen_func_getvar, + .alloc_size = sizeof(struct listen_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_socket = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/spawn.c b/app/src/main/jni/badvpn/ncd/modules/spawn.c new file mode 100644 index 0000000..4f68670 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/spawn.c @@ -0,0 +1,410 @@ +/** + * @file spawn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which starts a process from a process template on initialization, and + * stops it on deinitialization. + * + * Synopsis: + * spawn(string template_name, list args) + * + * Description: + * On initialization, creates a new process from the template named + * 'template_name', with arguments 'args'. On deinitialization, initiates termination + * of the process and waits for it to terminate. The process can access objects + * as seen from 'spawn' via the _caller special object. + * + * Synopsis: + * spawn::join() + * + * Description: + * A join() on a spawn() is like a depend() on a provide() which is located at the + * end of the spawned process. + * + * Variables: + * Exposes objects from the spawned process. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_WORKING 1 +#define STATE_UP 2 +#define STATE_WAITING 3 +#define STATE_WAITING_TERMINATING 4 +#define STATE_TERMINATING 5 + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + LinkedList0 clean_list; + LinkedList0 dirty_list; + int state; +}; + +struct join_instance { + NCDModuleInst *i; + struct instance *spawn; + LinkedList0Node list_node; + int is_dirty; +}; + +static void assert_dirty_state (struct instance *o); +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void bring_joins_down (struct instance *o); +static void continue_working (struct instance *o); +static void continue_terminating (struct instance *o); +static void instance_free (struct instance *o); + +static void assert_dirty_state (struct instance *o) +{ + ASSERT(!LinkedList0_IsEmpty(&o->dirty_list) == (o->state == STATE_WAITING || o->state == STATE_WAITING_TERMINATING)) +} + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + assert_dirty_state(o); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_WORKING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // set state up + o->state = STATE_UP; + + // bring joins up + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clean_list); ln; ln = LinkedList0Node_Next(ln)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + NCDModuleInst_Backend_Up(join->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_UP) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // bring joins down, moving them to the dirty list + bring_joins_down(o); + + // set state waiting + o->state = STATE_WAITING; + + // if we have no joins, continue immediately + if (LinkedList0_IsEmpty(&o->dirty_list)) { + continue_working(o); + return; + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // die finally + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void bring_joins_down (struct instance *o) +{ + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->clean_list)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + NCDModuleInst_Backend_Down(join->i); + LinkedList0_Remove(&o->clean_list, &join->list_node); + LinkedList0_Prepend(&o->dirty_list, &join->list_node); + join->is_dirty = 1; + } +} + +static void continue_working (struct instance *o) +{ + ASSERT(o->state == STATE_WAITING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // continue process + NCDModuleProcess_Continue(&o->process); + + // set state working + o->state = STATE_WORKING; +} + +static void continue_terminating (struct instance *o) +{ + ASSERT(o->state == STATE_WAITING_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // request process to terminate + NCDModuleProcess_Terminate(&o->process); + + // set state terminating + o->state = STATE_TERMINATING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up. + // Do it before creating the process so that the process starts initializing before our own process continues. + NCDModuleInst_Backend_Up(o->i); + + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name_arg, args_arg, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set object resolution function + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + + // init lists + LinkedList0_Init(&o->clean_list); + LinkedList0_Init(&o->dirty_list); + + // set state working + o->state = STATE_WORKING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // unlink joins + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->clean_list)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + LinkedList0_Remove(&o->clean_list, &join->list_node); + join->spawn = NULL; + } + + // free process + NCDModuleProcess_Free(&o->process); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_WAITING_TERMINATING) + ASSERT(o->state != STATE_TERMINATING) + assert_dirty_state(o); + + // bring joins down + if (o->state == STATE_UP) { + bring_joins_down(o); + } + + // set state waiting terminating + o->state = STATE_WAITING_TERMINATING; + + // start terminating now if possible + if (LinkedList0_IsEmpty(&o->dirty_list)) { + continue_terminating(o); + return; + } +} + +static void join_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct join_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + o->spawn = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + assert_dirty_state(o->spawn); + + LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node); + o->is_dirty = 0; + + if (o->spawn->state == STATE_UP) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void join_func_die (void *vo) +{ + struct join_instance *o = vo; + + if (o->spawn) { + assert_dirty_state(o->spawn); + + // remove from list + if (o->is_dirty) { + LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node); + } else { + LinkedList0_Remove(&o->spawn->clean_list, &o->list_node); + } + + if (o->is_dirty && LinkedList0_IsEmpty(&o->spawn->dirty_list)) { + ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING) + + if (o->spawn->state == STATE_WAITING) { + continue_working(o->spawn); + } else { + continue_terminating(o->spawn); + } + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int join_func_getobj (void *vo, NCD_string_id_t name, NCDObject *out) +{ + struct join_instance *o = vo; + + if (!o->spawn) { + return 0; + } + + return NCDModuleProcess_GetObj(&o->spawn->process, name, out); +} + +static void join_func_clean (void *vo) +{ + struct join_instance *o = vo; + + if (!(o->spawn && o->is_dirty)) { + return; + } + + assert_dirty_state(o->spawn); + ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING) + + LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node); + LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node); + o->is_dirty = 0; + + if (LinkedList0_IsEmpty(&o->spawn->dirty_list)) { + if (o->spawn->state == STATE_WAITING) { + continue_working(o->spawn); + } else { + continue_terminating(o->spawn); + } + } +} + +static struct NCDModule modules[] = { + { + .type = "spawn", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "synchronous_process", // deprecated name + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "spawn::join", + .func_new2 = join_func_new, + .func_die = join_func_die, + .func_getobj = join_func_getobj, + .func_clean = join_func_clean, + .alloc_size = sizeof(struct join_instance), + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_spawn = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/strcmp.c b/app/src/main/jni/badvpn/ncd/modules/strcmp.c new file mode 100644 index 0000000..e648719 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/strcmp.c @@ -0,0 +1,107 @@ +/** + * @file strcmp.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * String comparison module. + * + * Synopsis: strcmp(string str1, string str2) + * Variables: + * string (empty) - "true" if str1 and str2 are equal, "false" if not + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int result; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef str1_arg; + NCDValRef str2_arg; + if (!NCDVal_ListRead(params->args, 2, &str1_arg, &str2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str1_arg) || !NCDVal_IsString(str2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // compare + o->result = (NCDVal_Compare(str1_arg, str2_arg) == 0); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->result, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "strcmp", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_strcmp = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/substr.c b/app/src/main/jni/badvpn/ncd/modules/substr.c new file mode 100644 index 0000000..6a50b0c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/substr.c @@ -0,0 +1,167 @@ +/** + * @file substr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * substr(string str, string start [, string max]) + * + * Description: + * Extracts a substring from a string. The result is the longest substring which + * starts at the offset 'start' bytes into 'str', and is no longer than 'max' bytes. + * If 'max' is not provided, the result is the substring from the offset 'start' to + * the end. In any case, 'start' must not be greater than the length of 'str'. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct substr_instance { + NCDModuleInst *i; + const char *data; + size_t length; + int is_external; + BRefTarget *external_ref_target; +}; + +static void substr_func_new_common (void *vo, NCDModuleInst *i, const char *data, size_t length, int is_external, BRefTarget *external_ref_target) +{ + struct substr_instance *o = vo; + o->i = i; + + o->data = data; + o->length = length; + o->is_external = is_external; + o->external_ref_target = external_ref_target; + + NCDModuleInst_Backend_Up(i); +} + +static int substr_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct substr_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + if (o->is_external) { + *out = NCDVal_NewExternalString(mem, o->data, o->length, o->external_ref_target); + } else { + *out = NCDVal_NewStringBin(mem, (const uint8_t *)o->data, o->length); + } + return 1; + } + + return 0; +} + +static void func_new_substr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef str_arg; + NCDValRef start_arg; + NCDValRef max_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &str_arg, &start_arg) && + !NCDVal_ListRead(params->args, 3, &str_arg, &start_arg, &max_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg) || !NCDVal_IsString(start_arg) || + (!NCDVal_IsInvalid(max_arg) && !NCDVal_IsString(max_arg)) + ) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t start; + if (!ncd_read_uintmax(start_arg, &start) || start > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong size"); + goto fail0; + } + + uintmax_t max = SIZE_MAX; + if (!NCDVal_IsInvalid(max_arg)) { + if (!ncd_read_uintmax(max_arg, &max) || max > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong max"); + goto fail0; + } + } + + const char *str_data = NCDVal_StringData(str_arg); + size_t str_length = NCDVal_StringLength(str_arg); + + if (start > str_length) { + ModuleLog(i, BLOG_ERROR, "start is beyond the end of the string"); + goto fail0; + } + + const char *sub_data = str_data + start; + size_t sub_length = str_length - start; + if (sub_length > max) { + sub_length = max; + } + + int is_external = 0; + BRefTarget *external_ref_target = NULL; + + if (NCDVal_IsExternalString(str_arg)) { + is_external = 1; + external_ref_target = NCDVal_ExternalStringTarget(str_arg); + } + else if (NCDVal_IsIdString(str_arg)) { + is_external = 1; + } + + substr_func_new_common(vo, i, sub_data, sub_length, is_external, external_ref_target); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "substr", + .func_new2 = func_new_substr, + .func_getvar2 = substr_func_getvar, + .alloc_size = sizeof(struct substr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_substr = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_evdev.c b/app/src/main/jni/badvpn/ncd/modules/sys_evdev.c new file mode 100644 index 0000000..d848a89 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_evdev.c @@ -0,0 +1,348 @@ +/** + * @file sys_evdev.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Linux event device module. + * + * Synopsis: sys.evdev(string device) + * Description: reports input events from a Linux event device. Transitions up when an event is + * detected, and goes down waiting for the next event when sys.evdev::nextevent() is called. + * Variables: + * string type - symbolic event type (e.g. EV_KEY, EV_REL, EV_ABS), corresponding to + * (struct input_event).type, or "unknown" + * string value - event value (signed integer), equal to (struct input_event).value + * string code_numeric - numeric event code (unsigned integer), equal to + * (struct input_event).code + * string code - symbolic event code (e.g. KEY_ESC. KEY_1, KEY_2, BTN_LEFT), corrresponding + * to (struct input_event).code, or "unknown" + * + * Synopsis: sys.evdev::nextevent() + * Description: makes the evdev module transition down in order to report the next event. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "linux_input_names.h" + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + int evdev_fd; + BFileDescriptor bfd; + int processing; + struct input_event event; +}; + +static void instance_free (struct instance *o, int is_error); + +enum {STRING_VALUE, STRING_CODE_NUMERIC, STRING_CODE}; + +static const char *strings[] = { + "value", "code_numeric", "code", NULL +}; + +#define MAKE_LOOKUP_FUNC(_name_) \ +static const char * evdev_##_name_##_to_str (uint16_t type) \ +{ \ + if (type >= (sizeof(_name_##_names) / sizeof(_name_##_names[0])) || !_name_##_names[type]) { \ + return "unknown"; \ + } \ + return _name_##_names[type]; \ +} + +MAKE_LOOKUP_FUNC(type) +MAKE_LOOKUP_FUNC(key) +MAKE_LOOKUP_FUNC(rel) +MAKE_LOOKUP_FUNC(abs) +MAKE_LOOKUP_FUNC(sw) +MAKE_LOOKUP_FUNC(msc) +MAKE_LOOKUP_FUNC(led) +MAKE_LOOKUP_FUNC(rep) +MAKE_LOOKUP_FUNC(snd) +MAKE_LOOKUP_FUNC(ffstatus) + +static void device_handler (struct instance *o, int events) +{ + if (o->processing) { + ModuleLog(o->i, BLOG_ERROR, "device error"); + instance_free(o, 1); + return; + } + + int res = read(o->evdev_fd, &o->event, sizeof(o->event)); + if (res < 0) { + ModuleLog(o->i, BLOG_ERROR, "read failed"); + instance_free(o, 1); + return; + } + if (res != sizeof(o->event)) { + ModuleLog(o->i, BLOG_ERROR, "read wrong"); + instance_free(o, 1); + return; + } + + // stop reading + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, 0); + + // set processing + o->processing = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void device_nextevent (struct instance *o) +{ + ASSERT(o->processing) + + // start reading + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + + // set not processing + o->processing = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef device_arg; + if (!NCDVal_ListRead(params->args, 1, &device_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(device_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate device + NCDValNullTermString device_nts; + if (!NCDVal_StringNullTerminate(device_arg, &device_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // open device + o->evdev_fd = open(device_nts.data, O_RDONLY); + NCDValNullTermString_Free(&device_nts); + if (o->evdev_fd < 0) { + ModuleLog(o->i, BLOG_ERROR, "open failed"); + goto fail0; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->evdev_fd)) { + ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->evdev_fd, (BFileDescriptor_handler)device_handler, o); + if (!BReactor_AddFileDescriptor(o->i->params->iparams->reactor, &o->bfd)) { + ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + + // set not processing + o->processing = 0; + return; + +fail1: + if (close(o->evdev_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); + + // close device. + // Ignore close error which happens if the device is removed. + if (close(o->evdev_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->processing) + + if (name == NCD_STRING_TYPE) { + *out = NCDVal_NewString(mem, evdev_type_to_str(o->event.type)); + return 1; + } + + if (name == ModuleString(o->i, STRING_VALUE)) { + char str[50]; + snprintf(str, sizeof(str), "%"PRIi32, o->event.value); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (name == ModuleString(o->i, STRING_CODE_NUMERIC)) { + *out = ncd_make_uintmax(mem, o->event.code); + return 1; + } + + if (name == ModuleString(o->i, STRING_CODE)) { + const char *str = "unknown"; + + #define MAKE_CASE(_evname_, _name_) \ + case _evname_: \ + str = evdev_##_name_##_to_str(o->event.code); \ + break; + + switch (o->event.type) { + #ifdef EV_KEY + MAKE_CASE(EV_KEY, key) + #endif + #ifdef EV_REL + MAKE_CASE(EV_REL, rel) + #endif + #ifdef EV_ABS + MAKE_CASE(EV_ABS, abs) + #endif + #ifdef EV_SW + MAKE_CASE(EV_SW, sw) + #endif + #ifdef EV_MSC + MAKE_CASE(EV_MSC, msc) + #endif + #ifdef EV_LED + MAKE_CASE(EV_LED, led) + #endif + #ifdef EV_REP + MAKE_CASE(EV_REP, rep) + #endif + #ifdef EV_SND + MAKE_CASE(EV_SND, snd) + #endif + #ifdef EV_FF_STATUS + MAKE_CASE(EV_FF_STATUS, ffstatus) + #endif + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!mo->processing) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + device_nextevent(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.evdev", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.evdev::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_evdev = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_request_client.c b/app/src/main/jni/badvpn/ncd/modules/sys_request_client.c new file mode 100644 index 0000000..fe4a54c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_request_client.c @@ -0,0 +1,646 @@ +/** + * @file sys_request_client.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.request_client(string connect_addr) + * + * Description: + * Connects to a request server (sys.request_server()). + * Goes up when the connection, and dies with error when it is broken. + * When requested to die, dies immediately, breaking the connection. + * + * The connect address should be in the same format as for the socket module. + * In particular, it must be in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * + * Synopsis: + * sys.request_client::request(request_data, string reply_handler, string finished_handler, list args) + * + * Description: + * Sends a request to the server and dispatches replies to the provided handlers. + * + * The 'request_data' argument is sent as part of the request and is used by the server + * to determine what to do with the request. + * + * When a reply is received, a new template process is created from 'reply_handler' to process the + * reply. This process can access the reply data sent by the server using '_reply.data'. + * Similarly, if the server finishes the request, a process is created from 'finished_handler'. + * In both cases, the process can access objects as seen from the request statement via "_caller". + * Termination of these processes is initiated immediately after they completes. They are created + * synchronously - if a reply or a finished message arrives before a previous process is has + * finished, it is queued. Once the finished message has been processed by 'finished_handler', no + * more processes will be created. + * + * When the request statement is requested to terminate, it initiates termination of the current + * handler process and waits for it to terminate (if any is running), and then dies. + * If the corresponding client statement dies after being requested to die, or as a result of + * an error, the request statement will not react to this. It will dispatch any pending messages + * and then proceed to do nothing. In this case, if a finished message was not received, it will + * not be dispatched. + * + * The request statement may however die at any time due to errors. In this case, it will + * initiate termination of the current process and wait for it to terminate (if any) before dying. + * + * The request protocol and the server allow the client the abort requests at any time, and to + * have the client notified only after the request has been completely aborted (i.e. the handler + * process of sys.request_server() has deinitialized completely). This client implementation will + * automatically request abortion of active requests when the request statement is requested + * to die. However, the request statement will not wait for the abortion to finish before dying. + * This means, for instance, that if you initialize a request statement right after having + * deinitiazed it, the requests may overlap on the server side. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define CSTATE_CONNECTING 1 +#define CSTATE_CONNECTED 2 + +#define RRSTATE_SENDING_REQUEST 1 +#define RRSTATE_READY 2 +#define RRSTATE_GONE_BAD 3 +#define RRSTATE_GONE_GOOD 4 + +#define RPSTATE_NONE 1 +#define RPSTATE_WORKING 2 +#define RPSTATE_TERMINATING 3 + +#define RDSTATE_NONE 1 +#define RDSTATE_DYING 2 +#define RDSTATE_DYING_ERROR 3 + +struct instance { + NCDModuleInst *i; + NCDRequestClient client; + LinkedList0 requests_list; + int state; +}; + +struct request_instance { + NCDModuleInst *i; + NCDValRef reply_handler; + NCDValRef finished_handler; + NCDValRef args; + struct instance *client; + NCDRequestClientRequest request; + LinkedList0Node requests_list_node; + LinkedList1 replies_list; + NCDModuleProcess process; + int process_is_finished; + NCDValMem process_reply_mem; + NCDValRef process_reply_data; + int rstate; + int pstate; + int dstate; +}; + +struct reply { + LinkedList1Node replies_list_node; + NCDValMem mem; + NCDValRef val; +}; + +static void client_handler_error (struct instance *o); +static void client_handler_connected (struct instance *o); +static void request_handler_sent (struct request_instance *o); +static void request_handler_reply (struct request_instance *o, NCDValMem reply_mem, NCDValRef reply_value); +static void request_handler_finished (struct request_instance *o, int is_error); +static void request_process_handler_event (NCDModuleProcess *process, int event); +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int request_process_reply_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static void request_gone (struct request_instance *o, int is_bad); +static void request_terminate_process (struct request_instance *o); +static void request_die (struct request_instance *o, int is_error); +static void request_free_reply (struct request_instance *o, struct reply *r, int have_value); +static int request_init_reply_process (struct request_instance *o, NCDValMem reply_mem, NCDValSafeRef reply_data); +static int request_init_finished_process (struct request_instance *o); +static void instance_free (struct instance *o, int with_error); +static void request_instance_free (struct request_instance *o, int with_error); + +enum {STRING_REPLY, STRING_DATA}; + +static const char *strings[] = { + "_reply", "data", NULL +}; + +static void client_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "client error"); + + // free instance + instance_free(o, 1); +} + +static void client_handler_connected (struct instance *o) +{ + ASSERT(o->state == CSTATE_CONNECTING) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state connected + o->state = CSTATE_CONNECTED; +} + +static void request_handler_sent (struct request_instance *o) +{ + ASSERT(o->rstate == RRSTATE_SENDING_REQUEST) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set rstate ready + o->rstate = RRSTATE_READY; +} + +static void request_handler_reply (struct request_instance *o, NCDValMem reply_mem, NCDValRef reply_value) +{ + ASSERT(o->rstate == RRSTATE_READY) + + // queue reply if process is running + if (o->pstate != RPSTATE_NONE) { + struct reply *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail1; + } + r->mem = reply_mem; + r->val = NCDVal_Moved(&r->mem, reply_value); + LinkedList1_Append(&o->replies_list, &r->replies_list_node); + return; + } + + // start reply process + if (!request_init_reply_process(o, reply_mem, NCDVal_ToSafe(reply_value))) { + goto fail1; + } + + return; + +fail1: + NCDValMem_Free(&reply_mem); + request_die(o, 1); +} + +static void request_handler_finished (struct request_instance *o, int is_error) +{ + ASSERT(o->rstate == RRSTATE_SENDING_REQUEST || o->rstate == RRSTATE_READY) + ASSERT(is_error || o->rstate == RRSTATE_READY) + + if (is_error) { + ModuleLog(o->i, BLOG_ERROR, "received error reply"); + goto fail; + } + + // request gone good + request_gone(o, 0); + + // start process for reporting finished, if possible + if (o->pstate == RPSTATE_NONE) { + if (!request_init_finished_process(o)) { + goto fail; + } + } + + return; + +fail: + request_die(o, 1); +} + +static void request_process_handler_event (NCDModuleProcess *process, int event) +{ + struct request_instance *o = UPPER_OBJECT(process, struct request_instance, process); + ASSERT(o->pstate != RPSTATE_NONE) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->pstate == RPSTATE_WORKING) + + // request process termination + request_terminate_process(o); + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(0) + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->pstate == RPSTATE_TERMINATING) + ASSERT(o->rstate != RRSTATE_SENDING_REQUEST) + + // free process + NCDModuleProcess_Free(&o->process); + + // free reply data + if (!o->process_is_finished) { + NCDValMem_Free(&o->process_reply_mem); + } + + // set process state none + o->pstate = RPSTATE_NONE; + + // die finally if requested + if (o->dstate == RDSTATE_DYING || o->dstate == RDSTATE_DYING_ERROR) { + request_instance_free(o, o->dstate == RDSTATE_DYING_ERROR); + return; + } + + if (!LinkedList1_IsEmpty(&o->replies_list)) { + // get first reply + struct reply *r = UPPER_OBJECT(LinkedList1_GetFirst(&o->replies_list), struct reply, replies_list_node); + + // start reply process + if (!request_init_reply_process(o, r->mem, NCDVal_ToSafe(r->val))) { + goto fail; + } + + // free reply + request_free_reply(o, r, 0); + } + else if (o->rstate == RRSTATE_GONE_GOOD && !o->process_is_finished) { + // start process for reporting finished + if (!request_init_finished_process(o)) { + goto fail; + } + } + + return; + + fail: + request_die(o, 1); + } break; + } +} + +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct request_instance *o = UPPER_OBJECT(process, struct request_instance, process); + ASSERT(o->pstate != RPSTATE_NONE) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, request_process_caller_obj_func_getobj); + return 1; + } + + if (!o->process_is_finished && name == ModuleString(o->i, STRING_REPLY)) { + *out_object = NCDObject_Build(-1, o, request_process_reply_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct request_instance *o = NCDObject_DataPtr(obj); + ASSERT(o->pstate != RPSTATE_NONE) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int request_process_reply_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct request_instance *o = NCDObject_DataPtr(obj); + ASSERT(o->pstate != RPSTATE_NONE) + ASSERT(!o->process_is_finished) + + if (name == ModuleString(o->i, STRING_DATA)) { + *out = NCDVal_NewCopy(mem, o->process_reply_data); + return 1; + } + + return 0; +} + +static void request_gone (struct request_instance *o, int is_bad) +{ + ASSERT(o->rstate != RRSTATE_GONE_BAD) + ASSERT(o->rstate != RRSTATE_GONE_GOOD) + + // remove from requests list + LinkedList0_Remove(&o->client->requests_list, &o->requests_list_node); + + // free request + NCDRequestClientRequest_Free(&o->request); + + // set state over + o->rstate = (is_bad ? RRSTATE_GONE_BAD : RRSTATE_GONE_GOOD); +} + +static void request_terminate_process (struct request_instance *o) +{ + ASSERT(o->pstate == RPSTATE_WORKING) + + // request process termination + NCDModuleProcess_Terminate(&o->process); + + // set process state terminating + o->pstate = RPSTATE_TERMINATING; +} + +static void request_die (struct request_instance *o, int is_error) +{ + // if we have no process, die right away, else we have to wait for process to terminate + if (o->pstate == RPSTATE_NONE) { + request_instance_free(o, is_error); + return; + } + + // release request + if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) { + request_gone(o, 1); + } + + // initiate process termination, if needed + if (o->pstate != RPSTATE_TERMINATING) { + request_terminate_process(o); + } + + // set dstate + o->dstate = (is_error ? RDSTATE_DYING_ERROR : RDSTATE_DYING); +} + +static void request_free_reply (struct request_instance *o, struct reply *r, int have_value) +{ + // remove from replies list + LinkedList1_Remove(&o->replies_list, &r->replies_list_node); + + // free value + if (have_value) { + NCDValMem_Free(&r->mem); + } + + // free structure + free(r); +} + +static int request_init_reply_process (struct request_instance *o, NCDValMem reply_mem, NCDValSafeRef reply_data) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // set parameters + o->process_is_finished = 0; + o->process_reply_mem = reply_mem; + o->process_reply_data = NCDVal_FromSafe(&o->process_reply_mem, reply_data); + + // init process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->reply_handler, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&o->process, request_process_func_getspecialobj); + + // set process state working + o->pstate = RPSTATE_WORKING; + return 1; + +fail0: + return 0; +} + +static int request_init_finished_process (struct request_instance *o) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // set parameters + o->process_is_finished = 1; + + // init process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->finished_handler, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&o->process, request_process_func_getspecialobj); + + // set process state working + o->pstate = RPSTATE_WORKING; + return 1; + +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef connect_addr_arg; + if (!NCDVal_ListRead(params->args, 1, &connect_addr_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // read connect address + struct BConnection_addr addr; + if (!ncd_read_bconnection_addr(connect_addr_arg, &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong connect address"); + goto fail0; + } + + // init client + if (!NCDRequestClient_Init(&o->client, addr, i->params->iparams->reactor, o, + (NCDRequestClient_handler_error)client_handler_error, + (NCDRequestClient_handler_connected)client_handler_connected)) { + ModuleLog(o->i, BLOG_ERROR, "NCDRequestClient_Init failed"); + goto fail0; + } + + // init requests list + LinkedList0_Init(&o->requests_list); + + // set state connecting + o->state = CSTATE_CONNECTING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int with_error) +{ + // deal with requests + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->requests_list)) { + struct request_instance *req = UPPER_OBJECT(ln, struct request_instance, requests_list_node); + request_gone(req, 1); + } + + // free client + NCDRequestClient_Free(&o->client); + + if (with_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static void request_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct request_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef request_data_arg; + NCDValRef reply_handler_arg; + NCDValRef finished_handler_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &request_data_arg, &reply_handler_arg, &finished_handler_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(reply_handler_arg) || !NCDVal_IsString(finished_handler_arg) || + !NCDVal_IsList(args_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->reply_handler = reply_handler_arg; + o->finished_handler = finished_handler_arg; + o->args = args_arg; + + // get client + struct instance *client = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + o->client = client; + + // check client state + if (client->state != CSTATE_CONNECTED) { + ModuleLog(o->i, BLOG_ERROR, "client is not connected"); + goto fail0; + } + + // init request + if (!NCDRequestClientRequest_Init(&o->request, &client->client, request_data_arg, o, + (NCDRequestClientRequest_handler_sent)request_handler_sent, + (NCDRequestClientRequest_handler_reply)request_handler_reply, + (NCDRequestClientRequest_handler_finished)request_handler_finished)) { + ModuleLog(o->i, BLOG_ERROR, "NCDRequestClientRequest_Init failed"); + goto fail0; + } + + // add to requests list + LinkedList0_Prepend(&client->requests_list, &o->requests_list_node); + + // init replies list + LinkedList1_Init(&o->replies_list); + + // set state + o->rstate = RRSTATE_SENDING_REQUEST; + o->pstate = RPSTATE_NONE; + o->dstate = RDSTATE_NONE; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void request_instance_free (struct request_instance *o, int with_error) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // free replies + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->replies_list)) { + struct reply *r = UPPER_OBJECT(ln, struct reply, replies_list_node); + request_free_reply(o, r, 1); + } + + // release request + if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) { + request_gone(o, 1); + } + + if (with_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void request_func_die (void *vo) +{ + struct request_instance *o = vo; + + request_die(o, 0); +} + +static struct NCDModule modules[] = { + { + .type = "sys.request_client", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.request_client::request", + .func_new2 = request_func_new, + .func_die = request_func_die, + .alloc_size = sizeof(struct request_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_request_client = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_request_server.c b/app/src/main/jni/badvpn/ncd/modules/sys_request_server.c new file mode 100644 index 0000000..31bc431 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_request_server.c @@ -0,0 +1,792 @@ +/** + * @file sys_request_server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A simple a IPC interface for NCD to talk to other processes over a Unix socket. + * + * Synopsis: + * sys.request_server(listen_address, string request_handler_template, list args) + * + * Description: + * Initializes a request server on the given socket path. Requests are served by + * starting a template process for every request. Multiple such processes may + * exist simultaneously. Termination of these processess may be initiated at + * any time if the request server no longer needs the request in question served. + * The payload of a request is a value, and can be accessed as _request.data + * from within the handler process. Replies to the request can be sent using + * _request->reply(data); replies are values too. Finally, _request->finish() + * should be called to indicate that no further replies will be sent. Calling + * finish() will immediately initiate termination of the handler process. + * Requests can be sent to NCD using the badvpn-ncd-request program. + * + * The listen address should be in the same format as for the socket module. + * In particular, it must be in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * + * Predefined objects and variables in request_handler_template: + * _caller - provides access to objects as seen from the sys.request_server() + * command + * _request.data - the request payload as sent by the client + * string _request.client_addr - the address of the client. The form is + * like the second part of the sys.request_server() address format, e.g. + * {"ipv4", "1.2.3.4", "4000"}. + * + * Synopsis: + * sys.request_server.request::reply(reply_data) + * + * Synopsis: + * sys.request_server.request::finish() + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define SEND_PAYLOAD_MTU 32768 +#define RECV_PAYLOAD_MTU 32768 + +#define SEND_MTU (SEND_PAYLOAD_MTU + sizeof(struct requestproto_header)) +#define RECV_MTU (RECV_PAYLOAD_MTU + sizeof(struct requestproto_header)) + +struct instance { + NCDModuleInst *i; + NCDValRef request_handler_template; + NCDValRef args; + BListener listener; + LinkedList0 connections_list; + int dying; +}; + +#define CONNECTION_STATE_RUNNING 1 +#define CONNECTION_STATE_TERMINATING 2 + +struct reply; + +struct connection { + struct instance *inst; + LinkedList0Node connections_list_node; + BConnection con; + BAddr addr; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + PacketPassFifoQueue send_queue; + PacketStreamSender send_pss; + LinkedList0 requests_list; + LinkedList0 replies_list; + int state; +}; + +struct request { + struct connection *con; + uint32_t request_id; + LinkedList0Node requests_list_node; + NCDValMem request_data_mem; + NCDValRef request_data; + struct reply *end_reply; + NCDModuleProcess process; + int terminating; + int got_finished; +}; + +struct reply { + struct connection *con; + LinkedList0Node replies_list_node; + PacketPassFifoQueueFlow send_qflow; + PacketPassInterface *send_qflow_if; + uint8_t *send_buf; +}; + +static void listener_handler (struct instance *o); +static void connection_free (struct connection *c); +static void connection_free_link (struct connection *c); +static void connection_terminate (struct connection *c); +static void connection_con_handler (struct connection *c, int event); +static void connection_recv_decoder_handler_error (struct connection *c); +static void connection_recv_if_handler_send (struct connection *c, uint8_t *data, int data_len); +static int request_init (struct connection *c, uint32_t request_id, const uint8_t *data, int data_len); +static void request_free (struct request *r); +static struct request * find_request (struct connection *c, uint32_t request_id); +static void request_process_handler_event (NCDModuleProcess *process, int event); +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int request_process_request_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static void request_terminate (struct request *r); +static struct reply * reply_init (struct connection *c, uint32_t request_id, NCDValRef reply_data); +static void reply_start (struct reply *r, uint32_t type); +static void reply_free (struct reply *r); +static void reply_send_qflow_if_handler_done (struct reply *r); +static void instance_free (struct instance *o); + +enum {STRING_REQUEST, STRING_DATA, STRING_CLIENT_ADDR, + STRING_SYS_REQUEST_SERVER_REQUEST}; + +static const char *strings[] = { + "_request", "data", "client_addr", + "sys.request_server.request", NULL +}; + +static void listener_handler (struct instance *o) +{ + ASSERT(!o->dying) + + BReactor *reactor = o->i->params->iparams->reactor; + BPendingGroup *pg = BReactor_PendingGroup(reactor); + + struct connection *c = malloc(sizeof(*c)); + if (!c) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + c->inst = o; + + LinkedList0_Prepend(&o->connections_list, &c->connections_list_node); + + if (!BConnection_Init(&c->con, BConnection_source_listener(&o->listener, &c->addr), reactor, c, (BConnection_handler)connection_con_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + BConnection_SendAsync_Init(&c->con); + BConnection_RecvAsync_Init(&c->con); + StreamPassInterface *con_send_if = BConnection_SendAsync_GetIf(&c->con); + StreamRecvInterface *con_recv_if = BConnection_RecvAsync_GetIf(&c->con); + + PacketPassInterface_Init(&c->recv_if, RECV_MTU, (PacketPassInterface_handler_send)connection_recv_if_handler_send, c, pg); + + if (!PacketProtoDecoder_Init(&c->recv_decoder, con_recv_if, &c->recv_if, pg, c, (PacketProtoDecoder_handler_error)connection_recv_decoder_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + PacketStreamSender_Init(&c->send_pss, con_send_if, PACKETPROTO_ENCLEN(SEND_MTU), pg); + + PacketPassFifoQueue_Init(&c->send_queue, PacketStreamSender_GetInput(&c->send_pss), pg); + + LinkedList0_Init(&c->requests_list); + + LinkedList0_Init(&c->replies_list); + + c->state = CONNECTION_STATE_RUNNING; + + ModuleLog(o->i, BLOG_INFO, "connection initialized"); + return; + +fail2: + PacketPassInterface_Free(&c->recv_if); + BConnection_RecvAsync_Free(&c->con); + BConnection_SendAsync_Free(&c->con); + BConnection_Free(&c->con); +fail1: + LinkedList0_Remove(&o->connections_list, &c->connections_list_node); + free(c); +fail0: + return; +} + +static void connection_free (struct connection *c) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&c->requests_list)) + ASSERT(LinkedList0_IsEmpty(&c->replies_list)) + + LinkedList0_Remove(&o->connections_list, &c->connections_list_node); + free(c); +} + +static void connection_free_link (struct connection *c) +{ + PacketPassFifoQueue_PrepareFree(&c->send_queue); + + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&c->replies_list)) { + struct reply *r = UPPER_OBJECT(ln, struct reply, replies_list_node); + ASSERT(r->con == c) + reply_free(r); + } + + PacketPassFifoQueue_Free(&c->send_queue); + PacketStreamSender_Free(&c->send_pss); + PacketProtoDecoder_Free(&c->recv_decoder); + PacketPassInterface_Free(&c->recv_if); + BConnection_RecvAsync_Free(&c->con); + BConnection_SendAsync_Free(&c->con); + BConnection_Free(&c->con); +} + +static void connection_terminate (struct connection *c) +{ + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + for (LinkedList0Node *ln = LinkedList0_GetFirst(&c->requests_list); ln; ln = LinkedList0Node_Next(ln)) { + struct request *r = UPPER_OBJECT(ln, struct request, requests_list_node); + + if (!r->terminating) { + request_terminate(r); + } + } + + connection_free_link(c); + + c->state = CONNECTION_STATE_TERMINATING; + + if (LinkedList0_IsEmpty(&c->requests_list)) { + connection_free(c); + return; + } +} + +static void connection_con_handler (struct connection *c, int event) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + ModuleLog(o->i, BLOG_INFO, "connection closed"); + + connection_terminate(c); +} + +static void connection_recv_decoder_handler_error (struct connection *c) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + ModuleLog(o->i, BLOG_ERROR, "decoder error"); + + connection_terminate(c); +} + +static void connection_recv_if_handler_send (struct connection *c, uint8_t *data, int data_len) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_MTU) + + PacketPassInterface_Done(&c->recv_if); + + if (data_len < sizeof(struct requestproto_header)) { + ModuleLog(o->i, BLOG_ERROR, "missing requestproto header"); + goto fail; + } + + struct requestproto_header header; + memcpy(&header, data, sizeof(header)); + uint32_t request_id = ltoh32(header.request_id); + uint32_t type = ltoh32(header.type); + + switch (type) { + case REQUESTPROTO_TYPE_CLIENT_REQUEST: { + if (find_request(c, request_id)) { + ModuleLog(o->i, BLOG_ERROR, "request with the same ID already exists"); + goto fail; + } + + if (!request_init(c, request_id, data + sizeof(header), data_len - sizeof(header))) { + goto fail; + } + } break; + + case REQUESTPROTO_TYPE_CLIENT_ABORT: { + struct request *r = find_request(c, request_id); + if (!r) { + // this is expected if we finish before we get the abort + return; + } + + if (!r->terminating) { + request_terminate(r); + } + } break; + + default: + ModuleLog(o->i, BLOG_ERROR, "invalid requestproto type"); + goto fail; + } + + return; + +fail: + connection_terminate(c); +} + +static int request_init (struct connection *c, uint32_t request_id, const uint8_t *data, int data_len) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + ASSERT(!find_request(c, request_id)) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_PAYLOAD_MTU) + + struct request *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + r->con = c; + r->request_id = request_id; + + LinkedList0_Prepend(&c->requests_list, &r->requests_list_node); + + NCDValMem_Init(&r->request_data_mem); + + if (!NCDValParser_Parse((const char *)data, data_len, &r->request_data_mem, &r->request_data)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValParser_Parse failed"); + goto fail1; + } + + if (!(r->end_reply = reply_init(c, request_id, NCDVal_NewInvalid()))) { + goto fail1; + } + + if (!NCDModuleProcess_InitValue(&r->process, o->i, o->request_handler_template, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail3; + } + + NCDModuleProcess_SetSpecialFuncs(&r->process, request_process_func_getspecialobj); + + r->terminating = 0; + r->got_finished = 0; + + ModuleLog(o->i, BLOG_INFO, "request initialized"); + return 1; + +fail3: + reply_free(r->end_reply); +fail1: + NCDValMem_Free(&r->request_data_mem); + LinkedList0_Remove(&c->requests_list, &r->requests_list_node); + free(r); +fail0: + return 0; +} + +static void request_free (struct request *r) +{ + struct connection *c = r->con; + NCDModuleProcess_AssertFree(&r->process); + + if (c->state != CONNECTION_STATE_TERMINATING) { + uint32_t type = r->got_finished ? REQUESTPROTO_TYPE_SERVER_FINISHED : REQUESTPROTO_TYPE_SERVER_ERROR; + reply_start(r->end_reply, type); + } + + NCDModuleProcess_Free(&r->process); + NCDValMem_Free(&r->request_data_mem); + LinkedList0_Remove(&c->requests_list, &r->requests_list_node); + free(r); +} + +static struct request * find_request (struct connection *c, uint32_t request_id) +{ + for (LinkedList0Node *ln = LinkedList0_GetFirst(&c->requests_list); ln; ln = LinkedList0Node_Next(ln)) { + struct request *r = UPPER_OBJECT(ln, struct request, requests_list_node); + if (!r->terminating && r->request_id == request_id) { + return r; + } + } + + return NULL; +} + +static void request_process_handler_event (NCDModuleProcess *process, int event) +{ + struct request *r = UPPER_OBJECT(process, struct request, process); + struct connection *c = r->con; + struct instance *o = c->inst; + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(!r->terminating) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(!r->terminating) + + NCDModuleProcess_Continue(&r->process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(r->terminating) + + request_free(r); + + if (c->state == CONNECTION_STATE_TERMINATING && LinkedList0_IsEmpty(&c->requests_list)) { + connection_free(c); + + if (o->dying && LinkedList0_IsEmpty(&o->connections_list)) { + instance_free(o); + return; + } + } + } break; + + default: ASSERT(0); + } +} + +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct request *r = UPPER_OBJECT(process, struct request, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, r, NCDObject_no_getvar, request_process_caller_obj_func_getobj); + return 1; + } + + if (name == ModuleString(r->con->inst->i, STRING_REQUEST)) { + *out_object = NCDObject_Build(ModuleString(r->con->inst->i, STRING_SYS_REQUEST_SERVER_REQUEST), r, request_process_request_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct request *r = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(r->con->inst->i, name, out_object); +} + +static int request_process_request_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct request *r = NCDObject_DataPtr(obj); + + if (name == ModuleString(r->con->inst->i, STRING_DATA)) { + *out = NCDVal_NewCopy(mem, r->request_data); + return 1; + } + + if (name == ModuleString(r->con->inst->i, STRING_CLIENT_ADDR)) { + *out = ncd_make_baddr(r->con->addr, mem); + return 1; + } + + return 0; +} + +static void request_terminate (struct request *r) +{ + ASSERT(!r->terminating) + + NCDModuleProcess_Terminate(&r->process); + + r->terminating = 1; +} + +static struct reply * reply_init (struct connection *c, uint32_t request_id, NCDValRef reply_data) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + NCDVal_Assert(reply_data); + + struct reply *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + r->con = c; + + LinkedList0_Prepend(&c->replies_list, &r->replies_list_node); + + PacketPassFifoQueueFlow_Init(&r->send_qflow, &c->send_queue); + + r->send_qflow_if = PacketPassFifoQueueFlow_GetInput(&r->send_qflow); + PacketPassInterface_Sender_Init(r->send_qflow_if, (PacketPassInterface_handler_done)reply_send_qflow_if_handler_done, r); + + ExpString str; + if (!ExpString_Init(&str)) { + ModuleLog(o->i, BLOG_ERROR, "ExpString_Init failed"); + goto fail1; + } + + if (!ExpString_AppendZeros(&str, sizeof(struct packetproto_header) + sizeof(struct requestproto_header))) { + ModuleLog(o->i, BLOG_ERROR, "ExpString_AppendZeros failed"); + goto fail2; + } + + if (!NCDVal_IsInvalid(reply_data) && !NCDValGenerator_AppendGenerate(reply_data, &str)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValGenerator_AppendGenerate failed"); + goto fail2; + } + + size_t len = ExpString_Length(&str); + if (len > INT_MAX || len > PACKETPROTO_ENCLEN(SEND_MTU) || len - sizeof(struct packetproto_header) > UINT16_MAX) { + ModuleLog(o->i, BLOG_ERROR, "reply is too long"); + goto fail2; + } + + r->send_buf = (uint8_t *)ExpString_Get(&str); + + struct packetproto_header pp; + pp.len = htol16(len - sizeof(pp)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + + memcpy(r->send_buf, &pp, sizeof(pp)); + memcpy(r->send_buf + sizeof(pp), &rp, sizeof(rp)); + + return r; + +fail2: + ExpString_Free(&str); +fail1: + PacketPassFifoQueueFlow_Free(&r->send_qflow); + LinkedList0_Remove(&c->replies_list, &r->replies_list_node); + free(r); +fail0: + return NULL; +} + +static void reply_start (struct reply *r, uint32_t type) +{ + struct requestproto_header rp; + memcpy(&rp, r->send_buf + sizeof(struct packetproto_header), sizeof(rp)); + rp.type = htol32(type); + memcpy(r->send_buf + sizeof(struct packetproto_header), &rp, sizeof(rp)); + + struct packetproto_header pp; + memcpy(&pp, r->send_buf, sizeof(pp)); + + int len = ltoh16(pp.len) + sizeof(struct packetproto_header); + + PacketPassInterface_Sender_Send(r->send_qflow_if, r->send_buf, len); +} + +static void reply_free (struct reply *r) +{ + struct connection *c = r->con; + PacketPassFifoQueueFlow_AssertFree(&r->send_qflow); + + free(r->send_buf); + PacketPassFifoQueueFlow_Free(&r->send_qflow); + LinkedList0_Remove(&c->replies_list, &r->replies_list_node); + free(r); +} + +static void reply_send_qflow_if_handler_done (struct reply *r) +{ + reply_free(r); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef listen_addr_arg; + NCDValRef request_handler_template_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 3, &listen_addr_arg, &request_handler_template_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(request_handler_template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->request_handler_template = request_handler_template_arg; + o->args = args_arg; + + // read listen address + struct BConnection_addr addr; + if (!ncd_read_bconnection_addr(listen_addr_arg, &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong listen address"); + goto fail0; + } + + // init listener + if (!BListener_InitGeneric(&o->listener, addr, i->params->iparams->reactor, o, (BListener_handler)listener_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BListener_InitGeneric failed"); + goto fail0; + } + + // init connections list + LinkedList0_Init(&o->connections_list); + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(o->dying) + ASSERT(LinkedList0_IsEmpty(&o->connections_list)) + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // free listener + BListener_Free(&o->listener); + + // terminate connections + LinkedList0Node *next_ln; + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->connections_list); ln && (next_ln = LinkedList0Node_Next(ln)), ln; ln = next_ln) { + struct connection *c = UPPER_OBJECT(ln, struct connection, connections_list_node); + ASSERT(c->inst == o) + + if (c->state != CONNECTION_STATE_TERMINATING) { + connection_terminate(c); + } + } + + // set dying + o->dying = 1; + + // if no connections, die right away + if (LinkedList0_IsEmpty(&o->connections_list)) { + instance_free(o); + return; + } +} + +static void reply_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef reply_data; + if (!NCDVal_ListRead(params->args, 1, &reply_data)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail; + } + + NCDModuleInst_Backend_Up(i); + + struct request *r = params->method_user; + struct connection *c = r->con; + + if (r->terminating) { + ModuleLog(i, BLOG_ERROR, "request is dying, cannot submit reply"); + goto fail; + } + + struct reply *rpl = reply_init(c, r->request_id, reply_data); + if (!rpl) { + ModuleLog(i, BLOG_ERROR, "failed to submit reply"); + goto fail; + } + + reply_start(rpl, REQUESTPROTO_TYPE_SERVER_REPLY); + return; + +fail: + NCDModuleInst_Backend_DeadError(i); +} + +static void finish_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail; + } + + NCDModuleInst_Backend_Up(i); + + struct request *r = params->method_user; + + if (r->terminating) { + ModuleLog(i, BLOG_ERROR, "request is dying, cannot submit finished"); + goto fail; + } + + r->got_finished = 1; + + request_terminate(r); + return; + +fail: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.request_server", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.request_server.request::reply", + .func_new2 = reply_func_new + }, { + .type = "sys.request_server.request::finish", + .func_new2 = finish_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_request_server = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_start_process.c b/app/src/main/jni/badvpn/ncd/modules/sys_start_process.c new file mode 100644 index 0000000..f34e7b3 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_start_process.c @@ -0,0 +1,1266 @@ +/** + * @file sys_start_process.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.start_process(list command, string mode [, map options]) + * + * Options: + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * Must not be present if the process is being opened for reading. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + * "term_on_deinit":"false" - do not send SIGTERM to the process when this statement + * is requested to terminate + * "deinit_kill_time":milliseconds - how long to wait for the process to terminate + * after this statement is requested to terminate until we send SIGKILL. If this option + * is not present or is "never", SIGKILL will not be sent. If this option is empty, the + * process will be sent SIGKILL immediately when the statement is requested to terminate. + * + * Variables: + * is_error - "true" if there was an error starting the process, "false" if the process + * has been started successfully + * + * Synopsis: + * sys.start_process::wait() + * + * Variables: + * exit_status - the exit code if the process terminated normally, -1 if it terminated + * with a signal + * + * Synopsis: + * sys.start_process::terminate() + * sys.start_process::kill() + * + * Synopsis: + * sys.start_process::read_pipe() + * + * Description: + * Creates a read interface to the process's standard output. Data is read using the + * read() method on this object. Read errors are reported implicitly by this statement + * going down and the 'is_error' variable changing to "true". + * When read_pipe() is initialized for a process, it takes ownership of the read pipe + * to the process. When read_pipe() is requested to terminate, it will close the pipe. + * Attempting to initialize read_pipe() on a process which was not started with 'r' + * in the mode argument, or where another read_pipe() object has already taken ownership + * of the read pipe, will result in throwing an error to the interpreter. + * + * Variables: + * string is_error - "true" if there was a read error, "false" if not + * + * Synopsis: + * sys.start_process::read_pipe::read() + * + * Description: + * Reads some data. If a read error occurs, it is reported implicitly via the + * read_pipe() object going down. If end of file is reached, this and any future read() + * operations will indicate that via the 'not_eof' variable. It is guaranteed that after + * EOF is reached, the read_pipe() object will not go down to report any errors. + * WARNING: if a read() is requested to terminate before it has completed, the + * read_pipe() will become unusable and any read() invocation after that will + * throw an error to the interpreter. + * + * Variables: + * string (empty) - data that was read, or an empty string on EOF + * string not_eof - "true" is EOF was not reached, "false" if it was + * + * Synopsis: + * sys.start_process::write_pipe() + * + * Description: + * Creates a write interface to the process's standard input. Data is written using the + * write() method on this object. Write errors are reported implicitly by this statement + * going down and the ''is_error variable changing to "true". + * When write_pipe() is initialized for a process, it takes ownership of the write pipe + * to the process. When write_pipe() is requested to terminate, it will close the pipe + * (unless the close() has been used). + * Attempting to initialize write_pipe() on a process which was not started with 'w' + * in the mode argument, or where another write_pipe() object has already taken ownership + * of the write pope, will result in throwing an error to the interpreter. + * + * Variables: + * string is_error - "true" if there was a write error, "false" if not + * + * Synopsis: + * sys.start_process::write_pipe::write(string data) + * + * Description: + * Writes the given data. If a write error occurs, it is reported implicitly via the + * write_pipe() object going down. + * WARNING: if a write() is requested to terminate before it has completed, the + * write_pipe() will become unusable and any write() or close() invocation after + * that will throw an error to the interpreter. + * + * Synopsis: + * sys.start_process::write_pipe::close(string data) + * + * Description: + * Closes the write pipe. This will make whatever is reading the other end of the pipe + * encounter EOF after it has read any pending data. It is guaranteed that after the + * pipe is closed, the write_pipe() object will not go down to report any errors. + * After close() is performed, any further write() or close() calls are disallowed and + * will throw errors to the interpreter. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define READ_BUF_SIZE 8192 + +#define PROCESS_STATE_ERROR 1 +#define PROCESS_STATE_RUNNING 2 +#define PROCESS_STATE_TERMINATED 3 +#define PROCESS_STATE_DYING 4 + +#define READER_STATE_RUNNING 1 +#define READER_STATE_EOF 2 +#define READER_STATE_ERROR 3 +#define READER_STATE_ABORTED 4 + +#define WRITER_STATE_RUNNING 1 +#define WRITER_STATE_CLOSED 2 +#define WRITER_STATE_ERROR 3 +#define WRITER_STATE_ABORTED 4 + +struct process_instance { + NCDModuleInst *i; + BProcess process; + BSmallTimer kill_timer; + LinkedList0 waits_list; + btime_t deinit_kill_time; + int term_on_deinit; + int read_fd; + int write_fd; + int exit_status; + int state; +}; + +struct wait_instance { + NCDModuleInst *i; + struct process_instance *pinst; + LinkedList0Node waits_list_node; + int exit_status; +}; + +struct read_pipe_instance { + NCDModuleInst *i; + int state; + int read_fd; + BConnection connection; + NCDBufStore store; + struct read_instance *read_inst; +}; + +struct read_instance { + NCDModuleInst *i; + struct read_pipe_instance *read_pipe_inst; + NCDBuf *buf; + size_t read_size; +}; + +struct write_pipe_instance { + NCDModuleInst *i; + int state; + int write_fd; + BConnection connection; + struct write_instance *write_inst; +}; + +struct write_instance { + NCDModuleInst *i; + struct write_pipe_instance *write_pipe_inst; + b_cstring cstr; + size_t pos; +}; + +static int parse_mode (NCDModuleInst *i, NCDValRef mode_arg, int *out_read, int *out_write) +{ + if (!NCDVal_IsString(mode_arg)) { + ModuleLog(i, BLOG_ERROR, "mode argument must be a string"); + return 0; + } + + *out_read = 0; + *out_write = 0; + + b_cstring cstr = NCDVal_StringCstring(mode_arg); + + B_CSTRING_LOOP_CHARS(cstr, char_pos, ch, { + if (ch == 'r') { + *out_read = 1; + } + else if (ch == 'w') { + *out_write = 1; + } + else { + ModuleLog(i, BLOG_ERROR, "invalid character in mode argument"); + return 0; + } + }) + + return 1; +} + +static void process_free (struct process_instance *o) +{ + // close write fd + if (o->write_fd != -1) { + if (close(o->write_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + } + + // close read fd + if (o->read_fd != -1) { + if (close(o->read_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void process_handler (void *vo, int normally, uint8_t normally_exit_status) +{ + struct process_instance *o = vo; + ASSERT(o->state == PROCESS_STATE_RUNNING || o->state == PROCESS_STATE_DYING) + + ModuleLog(o->i, BLOG_INFO, "process terminated"); + + // free kill timer + BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &o->kill_timer); + + // free process + BProcess_Free(&o->process); + + // remember exit code + o->exit_status = (!normally ? -1 : normally_exit_status); + + // finish waits + LinkedList0Node *ln; + while ((ln = LinkedList0_GetFirst(&o->waits_list))) { + struct wait_instance *winst = UPPER_OBJECT(ln, struct wait_instance, waits_list_node); + ASSERT(winst->pinst == o) + LinkedList0_Remove(&o->waits_list, &winst->waits_list_node); + winst->pinst = NULL; + winst->exit_status = o->exit_status; + NCDModuleInst_Backend_Up(winst->i); + } + + // if we have been requested to die, then die now + if (o->state == PROCESS_STATE_DYING) { + process_free(o); + return; + } + + // set state + o->state = PROCESS_STATE_TERMINATED; +} + +static void process_kill_timer_handler (BSmallTimer *kill_timer) +{ + struct process_instance *o = UPPER_OBJECT(kill_timer, struct process_instance, kill_timer); + ASSERT(o->state == PROCESS_STATE_DYING) + + ModuleLog(o->i, BLOG_INFO, "killing process after timeout"); + BProcess_Kill(&o->process); +} + +static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val) +{ + struct process_instance *o = user; + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) { + o->term_on_deinit = ncd_read_boolean(val); + return 1; + } + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "deinit_kill_time")) { + if (NCDVal_StringEquals(val, "never")) { + o->deinit_kill_time = -2; + } + else if (NCDVal_StringEqualsId(val, NCD_STRING_EMPTY, o->i->params->iparams->string_index)) { + o->deinit_kill_time = -1; + } + else if (!ncd_read_time(val, &o->deinit_kill_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong value for deinit_kill_time option"); + return 0; + } + return 1; + } + + return 0; +} + +static void process_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct process_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + // check arguments + NCDValRef command_arg; + NCDValRef mode_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &command_arg, &mode_arg) && + !NCDVal_ListRead(params->args, 3, &command_arg, &mode_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // parse mode + int is_read; + int is_write; + if (!parse_mode(i, mode_arg, &is_read, &is_write)) { + goto fail0; + } + + // parse options + NCDBProcessOpts opts; + int keep_stdout; + int keep_stderr; + o->deinit_kill_time = -2; + o->term_on_deinit = 1; + if (!NCDBProcessOpts_Init2(&opts, options_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL, &keep_stdout, &keep_stderr)) { + goto fail0; + } + + // keep-stdout option and read mode are not compatible + if (keep_stdout && is_read) { + ModuleLog(i, BLOG_ERROR, "keep-stdout and read mode are not compatible"); + goto fail1; + } + + // prepare for creating pipes + int fds[4]; + int fds_map[3]; + int start_num_fds = opts.nfds; + int num_fds = start_num_fds; + memcpy(fds, opts.fds, num_fds * sizeof(int)); + memcpy(fds_map, opts.fds_map, num_fds * sizeof(int)); + int read_fd = -1; + int write_fd = -1; + + // create read pipe + if (is_read) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + ModuleLog(i, BLOG_ERROR, "pipe failed"); + goto error1; + } + read_fd = pipefd[0]; + fds[num_fds] = pipefd[1]; + fds_map[num_fds++] = STDOUT_FILENO; + } + + // create write pipe + if (is_write) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + ModuleLog(i, BLOG_ERROR, "pipe failed"); + goto error1; + } + write_fd = pipefd[1]; + fds[num_fds] = pipefd[0]; + fds_map[num_fds++] = STDIN_FILENO; + } + + // terminate fds array + fds[num_fds] = -1; + + // build process parameters struct + struct BProcess_params p_params = {}; + p_params.fds = fds; + p_params.fds_map = fds_map; + p_params.do_setsid = opts.do_setsid; + p_params.username = opts.username; + + // build command line + char *exec; + CmdLine cl; + if (!ncd_build_cmdline(i, BLOG_CURRENT_CHANNEL, command_arg, &exec, &cl)) { + goto error1; + } + + // start process + int res = BProcess_Init2(&o->process, i->params->iparams->manager, process_handler, o, exec, CmdLine_Get(&cl), p_params); + CmdLine_Free(&cl); + free(exec); + if (!res) { + ModuleLog(i, BLOG_ERROR, "BProcess_Init failed"); + goto error1; + } + + // init kill timer + BSmallTimer_Init(&o->kill_timer, process_kill_timer_handler); + + // close child fds + while (num_fds-- > start_num_fds) { + if (close(fds[num_fds]) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + + // free opts + NCDBProcessOpts_Free(&opts); + + // init waits list + LinkedList0_Init(&o->waits_list); + + // remember our fds + o->read_fd = read_fd; + o->write_fd = write_fd; + + // set state + o->state = PROCESS_STATE_RUNNING; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDBProcessOpts_Free(&opts); +fail0: + NCDModuleInst_Backend_DeadError(i); + return; + +error1: + if (write_fd != -1) { + if (close(write_fd) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + if (read_fd != -1) { + if (close(read_fd) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + while (num_fds-- > start_num_fds) { + if (close(fds[num_fds]) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + NCDBProcessOpts_Free(&opts); + + o->read_fd = -1; + o->write_fd = -1; + o->state = PROCESS_STATE_ERROR; + NCDModuleInst_Backend_Up(i); +} + +static void process_func_die (void *vo) +{ + struct process_instance *o = vo; + ASSERT(o->state != PROCESS_STATE_DYING) + + // if process is not running, die immediately + if (o->state != PROCESS_STATE_RUNNING) { + process_free(o); + return; + } + + if (o->term_on_deinit) { + ModuleLog(o->i, BLOG_INFO, "terminating process"); + + // send termination signal + BProcess_Terminate(&o->process); + } else { + ModuleLog(o->i, BLOG_INFO, "not terminating process as requested"); + } + + if (o->deinit_kill_time == -1) { + // user wants SIGKILL immediately + ModuleLog(o->i, BLOG_INFO, "killing process immediately"); + BProcess_Kill(&o->process); + } else if (o->deinit_kill_time >= 0) { + // user wants SIGKILL after some time + BReactor_SetSmallTimer(o->i->params->iparams->reactor, &o->kill_timer, BTIMER_SET_RELATIVE, o->deinit_kill_time); + } + + // set state + o->state = PROCESS_STATE_DYING; +} + +static int process_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct process_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == PROCESS_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void wait_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct wait_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->state == PROCESS_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "wait() is disallowed after the process has failed to start"); + goto fail0; + } + + if (pinst->state == PROCESS_STATE_TERMINATED) { + // not waiting, set no pinst + o->pinst = NULL; + + // remember exit code + o->exit_status = pinst->exit_status; + + // go up + NCDModuleInst_Backend_Up(i); + } else { + // waitint, set pinst + o->pinst = pinst; + + // insert to waits list + LinkedList0_Prepend(&pinst->waits_list, &o->waits_list_node); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void wait_func_die (void *vo) +{ + struct wait_instance *o = vo; + + // remove from waits list + if (o->pinst) { + LinkedList0_Remove(&o->pinst->waits_list, &o->waits_list_node); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int wait_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct wait_instance *o = vo; + ASSERT(!o->pinst) + + if (name == NCD_STRING_EXIT_STATUS) { + if (o->exit_status == -1) { + *out = NCDVal_NewString(mem, "-1"); + } else { + *out = ncd_make_uintmax(mem, o->exit_status); + } + return 1; + } + + return 0; +} + +static void terminate_kill_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_kill) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->state == PROCESS_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "terminate()/kill() is disallowed after the process has failed to start"); + goto fail0; + } + + if (pinst->state != PROCESS_STATE_TERMINATED) { + if (is_kill) { + BProcess_Kill(&pinst->process); + } else { + BProcess_Terminate(&pinst->process); + } + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void terminate_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + terminate_kill_new_common(vo, i, params, 0); +} + +static void kill_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + terminate_kill_new_common(vo, i, params, 1); +} + +static void read_pipe_free_connection (struct read_pipe_instance *o) +{ + // disconnect read instance + if (o->read_inst) { + ASSERT(o->read_inst->read_pipe_inst == o) + o->read_inst->read_pipe_inst = NULL; + } + + // free store + NCDBufStore_Free(&o->store); + + // free connection read interface + BConnection_RecvAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // close fd + if (close(o->read_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +} + +static void read_pipe_abort (struct read_pipe_instance *o) +{ + ASSERT(o->state == READER_STATE_RUNNING) + + // release connection resources + read_pipe_free_connection(o); + + // set state + o->state = READER_STATE_ABORTED; +} + +static void read_pipe_connection_handler (void *vo, int event) +{ + struct read_pipe_instance *o = vo; + ASSERT(o->state == READER_STATE_RUNNING) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + // if we have read operation, make it finish with eof + if (o->read_inst) { + ASSERT(o->read_inst->read_pipe_inst == o) + ASSERT(o->read_inst->buf) + o->read_inst->read_pipe_inst = NULL; + o->read_inst->read_size = 0; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; + } + + // free connection resources + read_pipe_free_connection(o); + + // set state closed + o->state = READER_STATE_EOF; + return; + } + + ModuleLog(o->i, BLOG_ERROR, "read pipe error"); + + // free connection resources + read_pipe_free_connection(o); + + // set state error + o->state = READER_STATE_ERROR; + + // backtrack + NCDModuleInst_Backend_DownUp(o->i); +} + +static void read_pipe_recv_handler_done (void *vo, int data_len) +{ + struct read_pipe_instance *o = vo; + ASSERT(o->state == READER_STATE_RUNNING) + ASSERT(o->read_inst) + ASSERT(o->read_inst->read_pipe_inst == o) + ASSERT(o->read_inst->buf) + ASSERT(data_len > 0) + ASSERT(data_len <= NCDBufStore_BufSize(&o->store)) + + // finish read operation + o->read_inst->read_pipe_inst = NULL; + o->read_inst->read_size = data_len; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; +} + +static void read_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_pipe_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->read_fd == -1) { + ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for reading or a read_pipe was already created"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->read_fd), i->params->iparams->reactor, o, read_pipe_connection_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection read interface + BConnection_RecvAsync_Init(&o->connection); + + // set recv done callback + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), read_pipe_recv_handler_done, o); + + // init store + NCDBufStore_Init(&o->store, READ_BUF_SIZE); + + // set variables + o->state = READER_STATE_RUNNING; + o->read_fd = pinst->read_fd; + o->read_inst = NULL; + + // steal read fd from process instance + pinst->read_fd = -1; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_pipe_func_die (void *vo) +{ + struct read_pipe_instance *o = vo; + + // free connection resources + if (o->state == READER_STATE_RUNNING) { + read_pipe_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_pipe_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == READER_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct read_pipe_instance *read_pipe_inst = params->method_user; + + // check if a read error has already occured + if (read_pipe_inst->state == READER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read error has occured"); + goto fail0; + } + + // check if the read_pipe has been aborted + if (read_pipe_inst->state == READER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read() has been aborted"); + goto fail0; + } + + // if EOF has already been encountered, complete the read immediately + if (read_pipe_inst->state == READER_STATE_EOF) { + o->buf = NULL; + o->read_pipe_inst = NULL; + o->read_size = 0; + NCDModuleInst_Backend_Up(i); + return; + } + + ASSERT(read_pipe_inst->state == READER_STATE_RUNNING) + + // check if there's already a read in progress + if (read_pipe_inst->read_inst) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed while another read() is in progress"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&read_pipe_inst->store); + if (!o->buf) { + ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // set read_pipe + o->read_pipe_inst = read_pipe_inst; + + // register read in read_pipe + read_pipe_inst->read_inst = o; + + // receive + size_t buf_size = NCDBufStore_BufSize(&read_pipe_inst->store); + int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size); + StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&read_pipe_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // if we're receiving, abort read_pipe + if (o->read_pipe_inst) { + ASSERT(o->read_pipe_inst->state == READER_STATE_RUNNING) + ASSERT(o->read_pipe_inst->read_inst == o) + ASSERT(o->buf) + read_pipe_abort(o->read_pipe_inst); + } + + // release buffer + if (o->buf) { + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + ASSERT(!o->read_pipe_inst) + ASSERT(!(o->read_size > 0) || o->buf) + + if (name == NCD_STRING_EMPTY) { + if (o->read_size > 0) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf)); + } else { + *out = NCDVal_NewIdString(mem, NCD_STRING_EMPTY, o->i->params->iparams->string_index); + } + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + int not_eof = (o->read_size > 0); + *out = ncd_make_boolean(mem, not_eof, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_pipe_free_connection (struct write_pipe_instance *o) +{ + // disconnect write instance + if (o->write_inst) { + ASSERT(o->write_inst->write_pipe_inst == o) + o->write_inst->write_pipe_inst = NULL; + } + + // free connection send interface + BConnection_SendAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // close fd + if (close(o->write_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +} + +static void write_pipe_abort (struct write_pipe_instance *o) +{ + ASSERT(o->state == WRITER_STATE_RUNNING) + + // release connection resources + write_pipe_free_connection(o); + + // set state + o->state = WRITER_STATE_ABORTED; +} + +static void write_pipe_close (struct write_pipe_instance *o) +{ + ASSERT(o->state == WRITER_STATE_RUNNING) + + // release connection resources + write_pipe_free_connection(o); + + // set state + o->state = WRITER_STATE_CLOSED; +} + +static void write_pipe_connection_handler (void *vo, int event) +{ + struct write_pipe_instance *o = vo; + ASSERT(o->state == WRITER_STATE_RUNNING) + + ModuleLog(o->i, BLOG_ERROR, "write pipe error"); + + // free connection resources + write_pipe_free_connection(o); + + // set state error + o->state = WRITER_STATE_ERROR; + + // backtrack + NCDModuleInst_Backend_DownUp(o->i); +} + +static void write_pipe_send_handler_done (void *vo, int data_len) +{ + struct write_pipe_instance *o = vo; + ASSERT(o->state == WRITER_STATE_RUNNING) + ASSERT(o->write_inst) + ASSERT(o->write_inst->write_pipe_inst == o) + ASSERT(data_len > 0) + ASSERT(data_len <= o->write_inst->cstr.length - o->write_inst->pos) + + struct write_instance *wr = o->write_inst; + + // update write progress + wr->pos += data_len; + + // if there is more data, start another write operation + if (wr->pos < wr->cstr.length) { + size_t chunk_length; + const char *chunk_data = b_cstring_get(wr->cstr, wr->pos, wr->cstr.length - wr->pos, &chunk_length); + size_t to_send = (chunk_length > INT_MAX ? INT_MAX : chunk_length); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)chunk_data, to_send); + return; + } + + // finish write operation + wr->write_pipe_inst = NULL; + NCDModuleInst_Backend_Up(wr->i); + o->write_inst = NULL; +} + +static void write_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_pipe_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->write_fd == -1) { + ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for writing or a write_pipe was already created"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->write_fd), i->params->iparams->reactor, o, write_pipe_connection_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection send interface + BConnection_SendAsync_Init(&o->connection); + + // set send done callback + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), write_pipe_send_handler_done, o); + + // set variables + o->state = WRITER_STATE_RUNNING; + o->write_fd = pinst->write_fd; + o->write_inst = NULL; + + // steal write fd from process instance + pinst->write_fd = -1; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_pipe_func_die (void *vo) +{ + struct write_pipe_instance *o = vo; + + // free connection resources + if (o->state == WRITER_STATE_RUNNING) { + write_pipe_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int write_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct write_pipe_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == WRITER_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_instance *o = vo; + o->i = i; + + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct write_pipe_instance *write_pipe_inst = params->method_user; + + // check if a write error has already occured + if (write_pipe_inst->state == WRITER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write error has occured"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write() has been aborted"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_CLOSED) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after close() has been called"); + goto fail0; + } + + ASSERT(write_pipe_inst->state == WRITER_STATE_RUNNING) + + // check if there's already a write in progress + if (write_pipe_inst->write_inst) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed while another write() is in progress"); + goto fail0; + } + + // initialize write progress state + o->cstr = NCDVal_StringCstring(data_arg); + o->pos = 0; + + // if there's nothing to send, go up immediately + if (o->cstr.length == 0) { + o->write_pipe_inst = NULL; + NCDModuleInst_Backend_Up(i); + return; + } + + // set write_pipe + o->write_pipe_inst = write_pipe_inst; + + // register write in write_pipe + write_pipe_inst->write_inst = o; + + // start send operation + size_t chunk_length; + const char *chunk_data = b_cstring_get(o->cstr, o->pos, o->cstr.length - o->pos, &chunk_length); + size_t to_send = (chunk_length > INT_MAX ? INT_MAX : chunk_length); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&write_pipe_inst->connection), (uint8_t *)chunk_data, to_send); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_func_die (void *vo) +{ + struct write_instance *o = vo; + + // if we're sending, abort write_pipe + if (o->write_pipe_inst) { + ASSERT(o->write_pipe_inst->state == WRITER_STATE_RUNNING) + ASSERT(o->write_pipe_inst->write_inst == o) + write_pipe_abort(o->write_pipe_inst); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct write_pipe_instance *write_pipe_inst = params->method_user; + + // check if a write error has already occured + if (write_pipe_inst->state == WRITER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write error has occured"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write() has been aborted"); + goto fail0; + } + + // check if the write_pipe has been closed + if (write_pipe_inst->state == WRITER_STATE_CLOSED) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after close() has been called"); + goto fail0; + } + + // close + write_pipe_close(write_pipe_inst); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.start_process", + .func_new2 = process_func_new, + .func_die = process_func_die, + .func_getvar2 = process_func_getvar, + .alloc_size = sizeof(struct process_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::wait", + .func_new2 = wait_func_new, + .func_die = wait_func_die, + .func_getvar2 = wait_func_getvar, + .alloc_size = sizeof(struct wait_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::terminate", + .func_new2 = terminate_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::kill", + .func_new2 = kill_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::read_pipe", + .func_new2 = read_pipe_func_new, + .func_die = read_pipe_func_die, + .func_getvar2 = read_pipe_func_getvar, + .alloc_size = sizeof(struct read_pipe_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::read_pipe::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe", + .func_new2 = write_pipe_func_new, + .func_die = write_pipe_func_die, + .func_getvar2 = write_pipe_func_getvar, + .alloc_size = sizeof(struct write_pipe_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe::write", + .func_new2 = write_func_new, + .func_die = write_func_die, + .alloc_size = sizeof(struct write_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_start_process = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_watch_directory.c b/app/src/main/jni/badvpn/ncd/modules/sys_watch_directory.c new file mode 100644 index 0000000..41f7d42 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_watch_directory.c @@ -0,0 +1,425 @@ +/** + * @file sys_watch_directory.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Directory watcher. + * + * Synopsis: sys.watch_directory(string dir) + * Description: reports directory entry events. Transitions up when an event is detected, and + * goes down waiting for the next event when sys.watch_directory::nextevent() is called. + * The directory is first scanned and "added" events are reported for all files. + * Variables: + * string event_type - what happened with the file: "added", "removed" or "changed" + * string filename - name of the file in the directory the event refers to + * string filepath - "dir/filename" + * + * Synopsis: sys.watch_directory::nextevent() + * Description: makes the watch_directory module transition down in order to report the next event. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define MAX_INOTIFY_EVENTS 128 + +struct instance { + NCDModuleInst *i; + NCDValNullTermString dir_nts; + DIR *dir_handle; + int inotify_fd; + BFileDescriptor bfd; + struct inotify_event events[MAX_INOTIFY_EVENTS]; + int events_count; + int events_index; + int processing; + const char *processing_file; + const char *processing_type; +}; + +static void instance_free (struct instance *o, int is_error); + +static void next_dir_event (struct instance *o) +{ + ASSERT(!o->processing) + ASSERT(o->dir_handle) + + struct dirent *entry; + + do { + // get next entry + errno = 0; + if (!(entry = readdir(o->dir_handle))) { + if (errno != 0) { + ModuleLog(o->i, BLOG_ERROR, "readdir failed"); + instance_free(o, 1); + return; + } + + // close directory + if (closedir(o->dir_handle) < 0) { + ModuleLog(o->i, BLOG_ERROR, "closedir failed"); + o->dir_handle = NULL; + instance_free(o, 1); + return; + } + + // set no dir handle + o->dir_handle = NULL; + + // start receiving inotify events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + return; + } + } while (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")); + + // set event + o->processing_file = entry->d_name; + o->processing_type = "added"; + o->processing = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void assert_inotify_event (struct instance *o) +{ + ASSERT(o->events_index < o->events_count) + ASSERT(o->events[o->events_index].len % sizeof(o->events[0]) == 0) + ASSERT(o->events[o->events_index].len / sizeof(o->events[0]) <= o->events_count - (o->events_index + 1)) +} + +static const char * translate_inotify_event (struct instance *o) +{ + assert_inotify_event(o); + + struct inotify_event *event = &o->events[o->events_index]; + + if (strlen(event->name) > 0) { + if ((event->mask & (IN_CREATE | IN_MOVED_TO))) { + return "added"; + } + if ((event->mask & (IN_DELETE | IN_MOVED_FROM))) { + return "removed"; + } + if ((event->mask & IN_MODIFY)) { + return "changed"; + } + } + + return NULL; +} + +static void skip_inotify_event (struct instance *o) +{ + assert_inotify_event(o); + + o->events_index += 1 + o->events[o->events_index].len / sizeof(o->events[0]); +} + +static void next_inotify_event (struct instance *o) +{ + ASSERT(!o->processing) + ASSERT(!o->dir_handle) + + // skip any bad events + while (o->events_index < o->events_count && !translate_inotify_event(o)) { + ModuleLog(o->i, BLOG_ERROR, "unknown inotify event"); + skip_inotify_event(o); + } + + if (o->events_index == o->events_count) { + // wait for more events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + return; + } + + // set event + o->processing_file = o->events[o->events_index].name; + o->processing_type = translate_inotify_event(o); + o->processing = 1; + + // consume this event + skip_inotify_event(o); + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void inotify_fd_handler (struct instance *o, int events) +{ + if (o->processing) { + ModuleLog(o->i, BLOG_ERROR, "file descriptor error"); + instance_free(o, 1); + return; + } + + ASSERT(!o->dir_handle) + + int res = read(o->inotify_fd, o->events, sizeof(o->events)); + if (res < 0) { + ModuleLog(o->i, BLOG_ERROR, "read failed"); + instance_free(o, 1); + return; + } + + // stop waiting for inotify events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, 0); + + ASSERT(res <= sizeof(o->events)) + ASSERT(res % sizeof(o->events[0]) == 0) + + // setup buffer state + o->events_count = res / sizeof(o->events[0]); + o->events_index = 0; + + // process inotify events + next_inotify_event(o); +} + +static void next_event (struct instance *o) +{ + ASSERT(o->processing) + + // set not processing + o->processing = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + + if (o->dir_handle) { + next_dir_event(o); + return; + } else { + next_inotify_event(o); + return; + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef dir_arg; + if (!NCDVal_ListRead(params->args, 1, &dir_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(dir_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate dir + if (!NCDVal_StringNullTerminate(dir_arg, &o->dir_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // open inotify + if ((o->inotify_fd = inotify_init()) < 0) { + ModuleLog(o->i, BLOG_ERROR, "inotify_init failed"); + goto fail1; + } + + // add watch + if (inotify_add_watch(o->inotify_fd, o->dir_nts.data, IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) { + ModuleLog(o->i, BLOG_ERROR, "inotify_add_watch failed"); + goto fail2; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->inotify_fd)) { + ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->inotify_fd, (BFileDescriptor_handler)inotify_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->i->params->iparams->reactor, &o->bfd)) { + ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + + // open directory + if (!(o->dir_handle = opendir(o->dir_nts.data))) { + ModuleLog(o->i, BLOG_ERROR, "opendir failed"); + goto fail3; + } + + // set not processing + o->processing = 0; + + // read first directory entry + next_dir_event(o); + return; + +fail3: + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); +fail2: + if (close(o->inotify_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +fail1: + NCDValNullTermString_Free(&o->dir_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // close directory + if (o->dir_handle) { + if (closedir(o->dir_handle) < 0) { + ModuleLog(o->i, BLOG_ERROR, "closedir failed"); + } + } + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); + + // close inotify + if (close(o->inotify_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + + // free dir nts + NCDValNullTermString_Free(&o->dir_nts); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->processing) + + if (!strcmp(name, "event_type")) { + *out = NCDVal_NewString(mem, o->processing_type); + return 1; + } + + if (!strcmp(name, "filename")) { + *out = NCDVal_NewString(mem, o->processing_file); + return 1; + } + + if (!strcmp(name, "filepath")) { + char *str = concat_strings(3, o->dir_nts.data, "/", o->processing_file); + if (!str) { + ModuleLog(o->i, BLOG_ERROR, "concat_strings failed"); + goto fail; + } + + *out = NCDVal_NewString(mem, str); + + free(str); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!mo->processing) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_directory", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_directory::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_directory = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_watch_input.c b/app/src/main/jni/badvpn/ncd/modules/sys_watch_input.c new file mode 100644 index 0000000..6039bee --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_watch_input.c @@ -0,0 +1,455 @@ +/** + * @file sys_watch_input.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Input device watcher. + * + * Synopsis: sys.watch_input(string devnode_type) + * Description: reports input device events. Transitions up when an event is detected, and + * goes down waiting for the next event when sys.watch_input::nextevent() is called. + * On startup, "added" events are reported for existing input devices. + * Arguments: + * string devnode_type - device node type, for example "event", "mouse" or "js". + * Variables: + * string event_type - what happened with the input device: "added" or "removed" + * string devname - device node path + * string device_type - input device type, "tablet", "joystick", "touchscreen", "mouse", + * "touchpad", "key", "keyboard" or "unknown" + * + * Synopsis: sys.watch_input::nextevent() + * Description: makes the watch_input module transition down in order to report the next event. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct device { + char *devname; + char *devpath; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + const char *devnode_type; + size_t devnode_type_len; + NCDUdevClient client; + LinkedList1 devices_list; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_devname (struct instance *o, const char *devname) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devname, devname)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free devname + free(device->devname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *devname, const char *device_type, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set devname + if (!BStringMap_Set(&map, "devname", devname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set device type + if (!BStringMap_Set(&map, "device_type", device_type)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static int devname_is_type (const char *devname, const char *devname_type, size_t devname_type_len) +{ + // skip digits at the end + size_t i; + for (i = strlen(devname); i > 0; i--) { + if (!isdigit(devname[i - 1])) { + break; + } + } + + // check if devname_type precedes skipped digits + for (size_t j = devname_type_len; j > 0; j--) { + if (!(i > 0 && devname[i - 1] == devname_type[j - 1])) { + return 0; + } + i--; + } + + // check if slash precedes devname_type + if (!(i > 0 && devname[i - 1] == '/')) { + return 0; + } + + return 1; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *devname, const char *devpath, const char *device_type) +{ + ASSERT(!find_device_by_devname(o, devname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init devname + if (!(device->devname = strdup(devname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // init removed map + if (!make_event_map(o, 0, devname, device_type, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, devname, device_type, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->devname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", devname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM"); + const char *devname = BStringMap_Get(cache_map, "DEVNAME"); + + if (!(subsystem && !strcmp(subsystem, "input") && devname && devname_is_type(devname, o->devnode_type, o->devnode_type_len))) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && strcmp(ex_device->devname, devname)) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_devname_device = find_device_by_devname(o, devname); + if (ex_devname_device) { + remove_device(o, ex_devname_device); + } + + // determine type + const char *device_type = "unknown"; + if (BStringMap_Get(cache_map, "ID_INPUT_TABLET")) { + device_type = "tablet"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_JOYSTICK")) { + device_type = "joystick"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHSCREEN")) { + device_type = "touchscreen"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_MOUSE")) { + device_type = "mouse"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHPAD")) { + device_type = "touchpad"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_KEY")) { + device_type = "key"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_KEYBOARD")) { + device_type = "keyboard"; + } + + add_device(o, devname, devpath, device_type); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef devnode_type_arg; + if (!NCDVal_ListRead(params->args, 1, &devnode_type_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(devnode_type_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->devnode_type = NCDVal_StringData(devnode_type_arg); + o->devnode_type_len = NCDVal_StringLength(devnode_type_arg); + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->devices_list)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + free_device(o, device, 1); + } + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_input", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_input::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_input = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/sys_watch_usb.c b/app/src/main/jni/badvpn/ncd/modules/sys_watch_usb.c new file mode 100644 index 0000000..d2e6b1c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/sys_watch_usb.c @@ -0,0 +1,421 @@ +/** + * @file sys_watch_usb.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * USB device watcher. + * + * Synopsis: sys.watch_usb() + * Description: reports USB device events. Transitions up when an event is detected, and + * goes down waiting for the next event when ->nextevent() is called. + * On startup, "added" events are reported for existing USB devices. + * + * Variables: + * string event_type - what happened with the USB device: "added" or "removed" + * string devname - device node path, e.g. /dev/bus/usb/XXX/YYY + * string vendor_id - vendor ID, e.g. 046d + * string model_id - model ID, e.g. c03e + * + * Synopsis: sys.watch_usb::nextevent() + * Description: makes the watch_usb module transition down in order to report the next event. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct device { + char *devname; + char *devpath; + uint16_t vendor_id; + uint16_t model_id; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + NCDUdevClient client; + LinkedList1 devices_list; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_devname (struct instance *o, const char *devname) +{ + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devname, devname)) { + return device; + } + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free devname + free(device->devname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *devname, uint16_t vendor_id, uint16_t model_id, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set devname + if (!BStringMap_Set(&map, "devname", devname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set vendor ID + char vendor_id_str[5]; + sprintf(vendor_id_str, "%04"PRIx16, vendor_id); + if (!BStringMap_Set(&map, "vendor_id", vendor_id_str)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set model ID + char model_id_str[5]; + sprintf(model_id_str, "%04"PRIx16, model_id); + if (!BStringMap_Set(&map, "model_id", model_id_str)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *devname, const char *devpath, uint16_t vendor_id, uint16_t model_id) +{ + ASSERT(!find_device_by_devname(o, devname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init devname + if (!(device->devname = strdup(devname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // set vendor and model ID + device->vendor_id = vendor_id; + device->model_id = model_id; + + // init removed map + if (!make_event_map(o, 0, devname, vendor_id, model_id, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, devname, vendor_id, model_id, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->devname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", devname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM"); + const char *devname = BStringMap_Get(cache_map, "DEVNAME"); + const char *devtype = BStringMap_Get(cache_map, "DEVTYPE"); + const char *vendor_id_str = BStringMap_Get(cache_map, "ID_VENDOR_ID"); + const char *model_id_str = BStringMap_Get(cache_map, "ID_MODEL_ID"); + + uintmax_t vendor_id; + uintmax_t model_id; + + if (!(subsystem && !strcmp(subsystem, "usb") && + devname && + devtype && !strcmp(devtype, "usb_device") && + vendor_id_str && parse_unsigned_hex_integer(vendor_id_str, &vendor_id) && + model_id_str && parse_unsigned_hex_integer(model_id_str, &model_id) + )) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && ( + strcmp(ex_device->devname, devname) || + ex_device->vendor_id != vendor_id || ex_device->model_id != model_id + )) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_devname_device = find_device_by_devname(o, devname); + if (ex_devname_device) { + remove_device(o, ex_devname_device); + } + + add_device(o, devname, devpath, vendor_id, model_id); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + while (!LinkedList1_IsEmpty(&o->devices_list)) { + struct device *device = UPPER_OBJECT(LinkedList1_GetFirst(&o->devices_list), struct device, devices_list_node); + free_device(o, device, 1); + } + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_usb", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_usb::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_usb = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/timer.c b/app/src/main/jni/badvpn/ncd/modules/timer.c new file mode 100644 index 0000000..6a748f9 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/timer.c @@ -0,0 +1,146 @@ +/** + * @file timer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * periodic_timer(string down_time, string up_time) + * + * Description: + * Periodically goes up and down. Starting in the down state, waits 'down_time' + * milliseconds. Then it goes up, and after 'up_time' milliseconds, goes back down, + * and the process repeats. When requested to terminate, terminates immedietely. + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_DOWN 1 +#define STATE_UP 2 + +struct instance { + NCDModuleInst *i; + btime_t down_time; + btime_t up_time; + BTimer timer; + int state; +}; + +static void timer_handler (void *vo) +{ + struct instance *o = vo; + + switch (o->state) { + case STATE_DOWN: { + o->state = STATE_UP; + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->up_time); + NCDModuleInst_Backend_Up(o->i); + } break; + + case STATE_UP: { + o->state = STATE_DOWN; + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->down_time); + NCDModuleInst_Backend_Down(o->i); + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef down_time_arg; + NCDValRef up_time_arg; + if (!NCDVal_ListRead(params->args, 2, &down_time_arg, &up_time_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(down_time_arg) || !NCDVal_IsString(up_time_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read times + if (!ncd_read_time(down_time_arg, &o->down_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong down time"); + goto fail0; + } + if (!ncd_read_time(up_time_arg, &o->up_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong up time"); + goto fail0; + } + + // init timer + BTimer_Init(&o->timer, 0, timer_handler, o); + + // set state down + o->state = STATE_DOWN; + + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->down_time); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "periodic_timer", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_timer = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/to_string.c b/app/src/main/jni/badvpn/ncd/modules/to_string.c new file mode 100644 index 0000000..2380c34 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/to_string.c @@ -0,0 +1,116 @@ +/** + * @file to_string.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * to_string(value) + * Variables: + * string (empty) - value, converted to string + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + char *str; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // convert to string + if (!(o->str = NCDValGenerator_Generate(value_arg))) { + ModuleLog(i, BLOG_ERROR, "NCDValGenerator_Generate failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free string + free(o->str); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewString(mem, o->str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "to_string", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_to_string = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/try.c b/app/src/main/jni/badvpn/ncd/modules/try.c new file mode 100644 index 0000000..4c4613c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/try.c @@ -0,0 +1,302 @@ +/** + * @file try.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * try(string template_name, list args) + * + * Description: + * Does the following: + * 1. Starts a template process from the specified template and arguments. + * 2. Waits for the process to initialize completely, or for a _try->assert() + * assertion to fail. + * 3. Initiates termination of the process and waits for it to terminate. + * 4. Goes to up state. The "succeeded" variable reflects whether the process + * managed to initialize, or an assertion failed. + * If at any point during these steps termination of the try statement is + * requested, requests the process to terminate (if not already), and dies + * when it terminates. + * + * Variables: + * string succeeded - "true" if the template process finished, "false" if assert + * was called. + * + * Synopsis: + * try.try::assert(string cond) + * + * Description: + * Call as _try->assert() from the template process. If cond is "true", + * does nothing. Else, initiates termination of the process (if not already), + * and marks the try operation as not succeeded. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + int state; + int dying; + int succeeded; +}; + +#define STATE_INIT 1 +#define STATE_DEINIT 2 +#define STATE_FINISHED 3 + +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void start_terminating (struct instance *o); +static void instance_free (struct instance *o); + +enum {STRING_TRY, STRING_TRY_TRY}; + +static const char *strings[] = { + "_try", "try.try", NULL +}; + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_INIT) + + // start terminating + start_terminating(o); + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_INIT) + + // continue + NCDModuleProcess_Continue(&o->process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_DEINIT) + + // free process + NCDModuleProcess_Free(&o->process); + + // die finally if requested + if (o->dying) { + instance_free(o); + return; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state finished + o->state = STATE_FINISHED; + } break; + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj); + return 1; + } + + if (name == ModuleString(o->i, STRING_TRY)) { + *out_object = NCDObject_Build(ModuleString(o->i, STRING_TRY_TRY), o, NCDObject_no_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void start_terminating (struct instance *o) +{ + ASSERT(o->state == STATE_INIT) + + // request process termination + NCDModuleProcess_Terminate(&o->process); + + // set state deinit + o->state = STATE_DEINIT; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // start process + if (!NCDModuleProcess_InitValue(&o->process, i, template_name_arg, args_arg, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special object function + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + + // set state init, not dying, assume succeeded + o->state = STATE_INIT; + o->dying = 0; + o->succeeded = 1; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // if we're finished, die immediately + if (o->state == STATE_FINISHED) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; + + // start terminating if not already + if (o->state == STATE_INIT) { + start_terminating(o); + } +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_FINISHED) + ASSERT(!o->dying) + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void assert_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef cond_arg; + if (!NCDVal_ListRead(params->args, 1, &cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsString(cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // get instance + struct instance *mo = params->method_user; + ASSERT(mo->state == STATE_INIT || mo->state == STATE_DEINIT) + + // signal up + NCDModuleInst_Backend_Up(i); + + if (!NCDVal_StringEquals(cond_arg, "true")) { + // mark not succeeded + mo->succeeded = 0; + + // start terminating if not already + if (mo->state == STATE_INIT) { + start_terminating(mo); + } + } + + return; + +fail1: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "try", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "try.try::assert", + .func_new2 = assert_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_try = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/value.c b/app/src/main/jni/badvpn/ncd/modules/value.c new file mode 100644 index 0000000..41ccf35 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/value.c @@ -0,0 +1,2102 @@ +/** + * @file value.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * value(value) + * value value::get(where) + * value value::try_get(where) + * value value::getpath(list path) + * value value::insert(where, what) + * value value::insert(what) + * value value::replace(where, what) + * value value::replace_this(value) + * value value::insert_undo(where, what) + * value value::insert_undo(what) + * value value::replace_undo(where, what) + * value value::replace_this_undo(value) + * + * Description: + * Value objects allow examining and manipulating values. + * These value objects are actually references to internal value structures, which + * may be shared between value objects. + * + * value(value) constructs a new value object from the given value. + * + * value::get(where) constructs a value object for the element at position 'where' + * (for a list), or the value corresponding to key 'where' (for a map). It is an + * error if the base value is not a list or a map, the index is out of bounds of + * the list, or the key does not exist in the map. + * The resulting value object is NOT a copy, and shares (part of) the same + * underlying value structure as the base value object. Deleting it will remove + * it from the list or map it is part of. + * + * value::try_get(where) is like get(), except that if any restriction on 'where' + * is violated, no error is triggered; instead, the value object is constructed + * as being deleted; this state is exposed via the 'exists' variable. + * This can be used to check for the presence of a key in a map, and in case it + * exists, allow access to the corresponding value without another get() statement. + * + * value::getpath(path) is like get(), except that it performs multiple + * consecutive resolutions. Also, if the path is an empty list, it performs + * no resulution at all. + * + * value::insert(where, what) constructs a value object by inserting into an + * existing value object. + * For lists, 'where' is the index of the element to insert before, or the length + * of the list to append to it. + * For maps, 'where' is the key to insert under. If the key already exists in the + * map, its value is replaced; any references to the old value however remain valid. + * + * value::insert(what) constructs a value object by appending to a list. An error + * is triggered if the base value is not a list. Assuming 'list' is a list value, + * list->insert(X) is equivalent to list->insert(list.length, X). + * + * value::replace(where, what) is like insert(), exept that, when inserting into a + * list, the value at the specified index is replaced with the new value (unless + * the index is equal to the length of the list). + * + * insert_undo() and replace_undo() are versions of insert() and replace() which + * attempt to revert the modifications when they deinitialize. + * Specifically, they work like that: + * - On initiialization, they take an internal reference to the value being replaced + * (if any; note that insert_undo() into a list never replaces a value). + * - On deinitialization, they remove the the inserted value from its parent (if there + * is one), and insert the old replaced value (to which a reference was kept) in that + * place (if any, and assuming it has not been deleted). + * Note that if the inserted value changes parents in between init and deinit, the + * result of undoing may be unexpected. + * + * Variables: + * (empty) - the value stored in the value object + * type - type of the value; "string", "list" or "map" + * length - number of elements in the list or map, or the number of bytes in a + * string + * keys - a list of keys in the map (only if the value is a map) + * exists - "true" or "false", reflecting whether the value object holds a value + * (is not in deleted state) + * + * Synopsis: + * value::remove(where) + * value::delete() + * + * Description: + * value::remove(where) removes from an existing value object. + * For lists, 'where' is the index of the element to remove, and must be in range. + * For maps, 'where' is the key to remove, and must be an existing key. + * In any case, any references to the removed value remain valid. + * + * value::delete() deletes the underlying value data of this value object. + * After delection, the value object enters a deleted state, which will cause any + * operation on it to fail. Any other value objects which referred to the same value + * or parts of it will too enter deleted state. If the value was an element + * in a list or map, is is removed from it. + * + * Synopsis: + * value value::substr(string start [, string length]) + * + * Description: + * Constructs a string value by extracting a part of a string. + * 'start' specifies the index of the character (from zero) where the substring to + * extract starts, and must be <= the length of the string. + * 'length' specifies the maximum number of characters to extract, if given. + * The newly constructed value is a copy of the extracted substring. + * The value must be a string value. + * + * Synopsis: + * value::reset(what) + * + * Description: + * Effectively deconstructs and reconstructs the value object. More precisely, + * it builds a new value structure from 'what', possibly invokes a scheduled undo + * operation (as scheduled by insert_undo() and replace_undo()), sets up this + * value object to reference the newly built value structure, without any scheduled + * undo operation. + * + * Synopsis: + * value::append(append_val) + * + * Description: + * Only defined when the existing value object is a string or list. If it is a string, + * appends the string 'append_val' to this string value; 'append_val' must be a string. + * If is is a list, inserts 'append_val' to the end of this list value. Unlike insert(), + * the resulting append() object is not itself a value object (which in case of insert() + * serves as a reference to the new value). + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define STOREDSTRING_TYPE (NCDVAL_STRING | (0 << 3)) +#define IDSTRING_TYPE (NCDVAL_STRING | (1 << 3)) +#define EXTERNALSTRING_TYPE (NCDVAL_STRING | (2 << 3)) +#define COMPOSEDSTRING_TYPE (NCDVAL_STRING | (3 << 3)) + +struct value; + +#include "value_maptree.h" +#include + +struct valref { + struct value *v; + LinkedList0Node refs_list_node; +}; + +typedef void (*value_deinit_func) (void *deinit_data, NCDModuleInst *i); + +struct instance { + NCDModuleInst *i; + struct valref ref; + value_deinit_func deinit_func; + void *deinit_data; +}; + +struct value { + LinkedList0 refs_list; + + struct value *parent; + union { + struct { + IndexedListNode list_contents_il_node; + } list_parent; + struct { + NCDValMem key_mem; + NCDValRef key; + MapTreeNode maptree_node; + } map_parent; + }; + + int type; + union { + struct { + char *data; + size_t length; + } storedstring; + struct { + NCD_string_id_t id; + NCDStringIndex *string_index; + } idstring; + struct { + const char *data; + size_t length; + BRefTarget *ref_target; + } externalstring; + struct { + NCDValComposedStringResource resource; + size_t offset; + size_t length; + } composedstring; + struct { + IndexedList list_contents_il; + } list; + struct { + MapTree map_tree; + } map; + }; +}; + +static const char * get_type_str (int type); +static void value_cleanup (struct value *v); +static void value_delete (struct value *v); +static struct value * value_init_storedstring (NCDModuleInst *i, const char *str, size_t len); +static struct value * value_init_idstring (NCDModuleInst *i, NCD_string_id_t id, NCDStringIndex *string_index); +static struct value * value_init_externalstring (NCDModuleInst *i, const char *data, size_t length, BRefTarget *ref_target); +static struct value * value_init_composedstring (NCDModuleInst *i, NCDValComposedStringResource resource, size_t offset, size_t length); +static int value_is_string (struct value *v); +static size_t value_string_length (struct value *v); +static void value_string_copy_out (struct value *v, char *dest); +static void value_string_set_allocd (struct value *v, char *data, size_t length); +static struct value * value_init_list (NCDModuleInst *i); +static size_t value_list_len (struct value *v); +static struct value * value_list_at (struct value *v, size_t index); +static size_t value_list_indexof (struct value *v, struct value *ev); +static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index); +static void value_list_remove (struct value *list, struct value *v); +static struct value * value_init_map (NCDModuleInst *i); +static size_t value_map_len (struct value *map); +static struct value * value_map_at (struct value *map, size_t index); +static struct value * value_map_find (struct value *map, NCDValRef key); +static int value_map_insert (struct value *map, struct value *v, NCDValMem mem, NCDValSafeRef key, NCDModuleInst *i); +static void value_map_remove (struct value *map, struct value *v); +static void value_map_remove2 (struct value *map, struct value *v, NCDValMem *out_mem, NCDValSafeRef *out_key); +static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValRef value); +static int value_to_value (NCDModuleInst *i, struct value *v, NCDValMem *mem, NCDValRef *out_value); +static struct value * value_get (NCDModuleInst *i, struct value *v, NCDValRef where, int no_error); +static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValRef path); +static struct value * value_insert (NCDModuleInst *i, struct value *v, NCDValRef where, NCDValRef what, int is_replace, struct value **out_oldv); +static struct value * value_insert_simple (NCDModuleInst *i, struct value *v, NCDValRef what); +static int value_remove (NCDModuleInst *i, struct value *v, NCDValRef where); +static int value_append (NCDModuleInst *i, struct value *v, NCDValRef data); +static void valref_init (struct valref *r, struct value *v); +static void valref_free (struct valref *r); +static struct value * valref_val (struct valref *r); +static void valref_break (struct valref *r); + +enum {STRING_EXISTS, STRING_KEYS}; + +static const char *strings[] = { + "exists", "keys", NULL +}; + +#include "value_maptree.h" +#include + +static const char * get_type_str (int type) +{ + switch (type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: + return "string"; + case NCDVAL_LIST: + return "list"; + case NCDVAL_MAP: + return "map"; + } + ASSERT(0) + return NULL; +} + +static void value_cleanup (struct value *v) +{ + if (v->parent || !LinkedList0_IsEmpty(&v->refs_list)) { + return; + } + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + case NCDVAL_LIST: { + while (value_list_len(v) > 0) { + struct value *ev = value_list_at(v, 0); + value_list_remove(v, ev); + value_cleanup(ev); + } + } break; + + case NCDVAL_MAP: { + while (value_map_len(v) > 0) { + struct value *ev = value_map_at(v, 0); + value_map_remove(v, ev); + value_cleanup(ev); + } + } break; + + default: ASSERT(0); + } + + free(v); +} + +static void value_delete (struct value *v) +{ + if (v->parent) { + switch (v->parent->type) { + case NCDVAL_LIST: { + value_list_remove(v->parent, v); + } break; + case NCDVAL_MAP: { + value_map_remove(v->parent, v); + } break; + default: ASSERT(0); + } + } + + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&v->refs_list)) { + struct valref *r = UPPER_OBJECT(ln, struct valref, refs_list_node); + ASSERT(r->v == v) + valref_break(r); + } + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + case NCDVAL_LIST: { + while (value_list_len(v) > 0) { + struct value *ev = value_list_at(v, 0); + value_delete(ev); + } + } break; + + case NCDVAL_MAP: { + while (value_map_len(v) > 0) { + struct value *ev = value_map_at(v, 0); + value_delete(ev); + } + } break; + + default: ASSERT(0); + } + + free(v); +} + +static struct value * value_init_storedstring (NCDModuleInst *i, const char *str, size_t len) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = STOREDSTRING_TYPE; + + if (!(v->storedstring.data = BAlloc(len))) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail1; + } + + if (str) { + memcpy(v->storedstring.data, str, len); + } + + v->storedstring.length = len; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static struct value * value_init_idstring (NCDModuleInst *i, NCD_string_id_t id, NCDStringIndex *string_index) +{ + ASSERT(string_index == i->params->iparams->string_index) + + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = IDSTRING_TYPE; + + v->idstring.id = id; + v->idstring.string_index = string_index; + + return v; + +fail0: + return NULL; +} + +static struct value * value_init_externalstring (NCDModuleInst *i, const char *data, size_t length, BRefTarget *ref_target) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (ref_target) { + if (!BRefTarget_Ref(ref_target)) { + ModuleLog(i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail1; + } + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = EXTERNALSTRING_TYPE; + + v->externalstring.data = data; + v->externalstring.length = length; + v->externalstring.ref_target = ref_target; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static struct value * value_init_composedstring (NCDModuleInst *i, NCDValComposedStringResource resource, size_t offset, size_t length) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (resource.ref_target) { + if (!BRefTarget_Ref(resource.ref_target)) { + ModuleLog(i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail1; + } + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = COMPOSEDSTRING_TYPE; + + v->composedstring.resource = resource; + v->composedstring.offset = offset; + v->composedstring.length = length; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static int value_is_string (struct value *v) +{ + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: + return 1; + default: + return 0; + } +} + +static size_t value_string_length (struct value *v) +{ + ASSERT(value_is_string(v)) + + switch (v->type) { + case STOREDSTRING_TYPE: + return v->storedstring.length; + case IDSTRING_TYPE: + return NCDStringIndex_Length(v->idstring.string_index, v->idstring.id); + case EXTERNALSTRING_TYPE: + return v->externalstring.length; + case COMPOSEDSTRING_TYPE: + return v->composedstring.length; + default: + ASSERT(0); + return 0; + } +} + +static void value_string_copy_out (struct value *v, char *dest) +{ + ASSERT(value_is_string(v)) + + switch (v->type) { + case STOREDSTRING_TYPE: + memcpy(dest, v->storedstring.data, v->storedstring.length); + break; + case IDSTRING_TYPE: + memcpy(dest, NCDStringIndex_Value(v->idstring.string_index, v->idstring.id), NCDStringIndex_Length(v->idstring.string_index, v->idstring.id)); + break; + case EXTERNALSTRING_TYPE: + memcpy(dest, v->externalstring.data, v->externalstring.length); + break; + case COMPOSEDSTRING_TYPE: { + b_cstring cstr = NCDValComposedStringResource_Cstring(v->composedstring.resource, v->composedstring.offset, v->composedstring.length); + b_cstring_copy_to_buf(cstr, 0, cstr.length, dest); + } break; + default: + ASSERT(0); + } +} + +static void value_string_set_allocd (struct value *v, char *data, size_t length) +{ + ASSERT(value_is_string(v)) + ASSERT(data) + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + default: + ASSERT(0); + } + + v->type = STOREDSTRING_TYPE; + v->storedstring.data = data; + v->storedstring.length = length; +} + +static struct value * value_init_list (NCDModuleInst *i) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + return NULL; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = NCDVAL_LIST; + + IndexedList_Init(&v->list.list_contents_il); + + return v; +} + +static size_t value_list_len (struct value *v) +{ + ASSERT(v->type == NCDVAL_LIST) + + return IndexedList_Count(&v->list.list_contents_il); +} + +static struct value * value_list_at (struct value *v, size_t index) +{ + ASSERT(v->type == NCDVAL_LIST) + ASSERT(index < value_list_len(v)) + + IndexedListNode *iln = IndexedList_GetAt(&v->list.list_contents_il, index); + ASSERT(iln) + + struct value *e = UPPER_OBJECT(iln, struct value, list_parent.list_contents_il_node); + ASSERT(e->parent == v) + + return e; +} + +static size_t value_list_indexof (struct value *v, struct value *ev) +{ + ASSERT(v->type == NCDVAL_LIST) + ASSERT(ev->parent == v) + + uint64_t index = IndexedList_IndexOf(&v->list.list_contents_il, &ev->list_parent.list_contents_il_node); + ASSERT(index < value_list_len(v)) + + return index; +} + +static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index) +{ + ASSERT(list->type == NCDVAL_LIST) + ASSERT(!v->parent) + ASSERT(index <= value_list_len(list)) + + if (value_list_len(list) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "list has too many elements"); + return 0; + } + + IndexedList_InsertAt(&list->list.list_contents_il, &v->list_parent.list_contents_il_node, index); + v->parent = list; + + return 1; +} + +static void value_list_remove (struct value *list, struct value *v) +{ + ASSERT(list->type == NCDVAL_LIST) + ASSERT(v->parent == list) + + IndexedList_Remove(&list->list.list_contents_il, &v->list_parent.list_contents_il_node); + v->parent = NULL; +} + +static struct value * value_init_map (NCDModuleInst *i) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + return NULL; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = NCDVAL_MAP; + + MapTree_Init(&v->map.map_tree); + + return v; +} + +static size_t value_map_len (struct value *map) +{ + ASSERT(map->type == NCDVAL_MAP) + + return MapTree_Count(&map->map.map_tree, 0); +} + +static struct value * value_map_at (struct value *map, size_t index) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(index < value_map_len(map)) + + struct value *e = MapTree_GetAt(&map->map.map_tree, 0, index); + ASSERT(e) + ASSERT(e->parent == map) + + return e; +} + +static struct value * value_map_find (struct value *map, NCDValRef key) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(NCDVal_Type(key)) + + struct value *e = MapTree_LookupExact(&map->map.map_tree, 0, key); + ASSERT(!e || e->parent == map) + + return e; +} + +static int value_map_insert (struct value *map, struct value *v, NCDValMem mem, NCDValSafeRef key, NCDModuleInst *i) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(!v->parent) + ASSERT((NCDVal_Type(NCDVal_FromSafe(&mem, key)), 1)) + ASSERT(!value_map_find(map, NCDVal_FromSafe(&mem, key))) + + if (value_map_len(map) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "map has too many elements"); + return 0; + } + + v->map_parent.key_mem = mem; + v->map_parent.key = NCDVal_FromSafe(&v->map_parent.key_mem, key); + int res = MapTree_Insert(&map->map.map_tree, 0, v, NULL); + ASSERT_EXECUTE(res) + v->parent = map; + + return 1; +} + +static void value_map_remove (struct value *map, struct value *v) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(v->parent == map) + + MapTree_Remove(&map->map.map_tree, 0, v); + NCDValMem_Free(&v->map_parent.key_mem); + v->parent = NULL; +} + +static void value_map_remove2 (struct value *map, struct value *v, NCDValMem *out_mem, NCDValSafeRef *out_key) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(v->parent == map) + ASSERT(out_mem) + ASSERT(out_key) + + MapTree_Remove(&map->map.map_tree, 0, v); + *out_mem = v->map_parent.key_mem; + *out_key = NCDVal_ToSafe(v->map_parent.key); + v->parent = NULL; +} + +static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValRef value) +{ + ASSERT((NCDVal_Type(value), 1)) + + struct value *v; + + switch (NCDVal_Type(value)) { + case NCDVAL_STRING: { + if (NCDVal_IsStoredString(value)) { + v = value_init_storedstring(i, NCDVal_StringData(value), NCDVal_StringLength(value)); + } else if (NCDVal_IsIdString(value)) { + v = value_init_idstring(i, NCDVal_IdStringId(value), NCDVal_IdStringStringIndex(value)); + } else if (NCDVal_IsExternalString(value)) { + v = value_init_externalstring(i, NCDVal_StringData(value), NCDVal_StringLength(value), NCDVal_ExternalStringTarget(value)); + } else if (NCDVal_IsComposedString(value)) { + v = value_init_composedstring(i, NCDVal_ComposedStringResource(value), NCDVal_ComposedStringOffset(value), NCDVal_StringLength(value)); + } else { + size_t length = NCDVal_StringLength(value); + v = value_init_storedstring(i, NULL, length); + if (v) { + NCDVal_StringCopyOut(value, 0, length, v->storedstring.data); + } + } + if (!v) { + goto fail0; + } + } break; + + case NCDVAL_LIST: { + if (!(v = value_init_list(i))) { + goto fail0; + } + + size_t count = NCDVal_ListCount(value); + + for (size_t j = 0; j < count; j++) { + struct value *ev = value_init_fromvalue(i, NCDVal_ListGet(value, j)); + if (!ev) { + goto fail1; + } + + if (!value_list_insert(i, v, ev, value_list_len(v))) { + value_cleanup(ev); + goto fail1; + } + } + } break; + + case NCDVAL_MAP: { + if (!(v = value_init_map(i))) { + goto fail0; + } + + for (NCDValMapElem e = NCDVal_MapFirst(value); !NCDVal_MapElemInvalid(e); e = NCDVal_MapNext(value, e)) { + NCDValRef ekey = NCDVal_MapElemKey(value, e); + NCDValRef eval = NCDVal_MapElemVal(value, e); + + NCDValMem key_mem; + NCDValMem_Init(&key_mem); + + NCDValRef key = NCDVal_NewCopy(&key_mem, ekey); + if (NCDVal_IsInvalid(key)) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + struct value *ev = value_init_fromvalue(i, eval); + if (!ev) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + if (!value_map_insert(v, ev, key_mem, NCDVal_ToSafe(key), i)) { + NCDValMem_Free(&key_mem); + value_cleanup(ev); + goto fail1; + } + } + } break; + + default: + ASSERT(0); + return NULL; + } + + return v; + +fail1: + value_cleanup(v); +fail0: + return NULL; +} + +static int value_to_value (NCDModuleInst *i, struct value *v, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(mem) + ASSERT(out_value) + + switch (v->type) { + case STOREDSTRING_TYPE: { + *out_value = NCDVal_NewStringBin(mem, (const uint8_t *)v->storedstring.data, v->storedstring.length); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case IDSTRING_TYPE: { + *out_value = NCDVal_NewIdString(mem, v->idstring.id, v->idstring.string_index); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case EXTERNALSTRING_TYPE: { + *out_value = NCDVal_NewExternalString(mem, v->externalstring.data, v->externalstring.length, v->externalstring.ref_target); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case COMPOSEDSTRING_TYPE: { + *out_value = NCDVal_NewComposedString(mem, v->composedstring.resource, v->composedstring.offset, v->composedstring.length); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case NCDVAL_LIST: { + *out_value = NCDVal_NewList(mem, value_list_len(v)); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + + for (size_t index = 0; index < value_list_len(v); index++) { + NCDValRef eval; + if (!value_to_value(i, value_list_at(v, index), mem, &eval)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out_value, eval)) { + goto fail; + } + } + } break; + + case NCDVAL_MAP: { + *out_value = NCDVal_NewMap(mem, value_map_len(v)); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + + for (size_t index = 0; index < value_map_len(v); index++) { + struct value *ev = value_map_at(v, index); + + NCDValRef key = NCDVal_NewCopy(mem, ev->map_parent.key); + if (NCDVal_IsInvalid(key)) { + goto fail; + } + + NCDValRef val; + if (!value_to_value(i, ev, mem, &val)) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(*out_value, key, val, &inserted)) { + goto fail; + } + ASSERT_EXECUTE(inserted) + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static struct value * value_get (NCDModuleInst *i, struct value *v, NCDValRef where, int no_error) +{ + ASSERT((NCDVal_Type(where), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + if (!no_error) ModuleLog(i, BLOG_ERROR, "cannot resolve into a string"); + goto fail; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "index is not a valid number (resolving into list)"); + goto fail; + } + + if (index >= value_list_len(v)) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "index is out of bounds (resolving into list)"); + goto fail; + } + + v = value_list_at(v, index); + } break; + + case NCDVAL_MAP: { + v = value_map_find(v, where); + if (!v) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "key does not exist (resolving into map)"); + goto fail; + } + } break; + + default: ASSERT(0); + } + + return v; + +fail: + return NULL; +} + +static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValRef path) +{ + ASSERT(NCDVal_IsList(path)) + + size_t count = NCDVal_ListCount(path); + + for (size_t j = 0; j < count; j++) { + if (!(v = value_get(i, v, NCDVal_ListGet(path, j), 0))) { + goto fail; + } + } + + return v; + +fail: + return NULL; +} + +static struct value * value_insert (NCDModuleInst *i, struct value *v, NCDValRef where, NCDValRef what, int is_replace, struct value **out_oldv) +{ + ASSERT(v) + ASSERT((NCDVal_Type(where), 1)) + ASSERT((NCDVal_Type(what), 1)) + ASSERT(is_replace == !!is_replace) + + struct value *nv = value_init_fromvalue(i, what); + if (!nv) { + goto fail0; + } + + struct value *oldv = NULL; + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + ModuleLog(i, BLOG_ERROR, "cannot insert into a string"); + goto fail1; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + ModuleLog(i, BLOG_ERROR, "index is not a valid number (inserting into list)"); + goto fail1; + } + + if (index > value_list_len(v)) { + ModuleLog(i, BLOG_ERROR, "index is out of bounds (inserting into list)"); + goto fail1; + } + + if (is_replace && index < value_list_len(v)) { + oldv = value_list_at(v, index); + + value_list_remove(v, oldv); + + int res = value_list_insert(i, v, nv, index); + ASSERT_EXECUTE(res) + } else { + if (!value_list_insert(i, v, nv, index)) { + goto fail1; + } + } + } break; + + case NCDVAL_MAP: { + oldv = value_map_find(v, where); + + if (!oldv && value_map_len(v) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "map has too many elements"); + goto fail1; + } + + NCDValMem key_mem; + NCDValMem_Init(&key_mem); + + NCDValRef key = NCDVal_NewCopy(&key_mem, where); + if (NCDVal_IsInvalid(key)) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + if (oldv) { + value_map_remove(v, oldv); + } + + int res = value_map_insert(v, nv, key_mem, NCDVal_ToSafe(key), i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + + if (out_oldv) { + *out_oldv = oldv; + } + else if (oldv) { + value_cleanup(oldv); + } + + return nv; + +fail1: + value_cleanup(nv); +fail0: + return NULL; +} + +static struct value * value_insert_simple (NCDModuleInst *i, struct value *v, NCDValRef what) +{ + ASSERT(v) + ASSERT((NCDVal_Type(what), 1)) + + struct value *nv = value_init_fromvalue(i, what); + if (!nv) { + goto fail0; + } + + switch (v->type) { + case NCDVAL_LIST: { + if (!value_list_insert(i, v, nv, value_list_len(v))) { + goto fail1; + } + } break; + + default: + ModuleLog(i, BLOG_ERROR, "one-argument insert is only defined for lists"); + return NULL; + } + + return nv; + +fail1: + value_cleanup(nv); +fail0: + return NULL; +} + +static int value_remove (NCDModuleInst *i, struct value *v, NCDValRef where) +{ + ASSERT(v) + ASSERT((NCDVal_Type(where), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + ModuleLog(i, BLOG_ERROR, "cannot remove from a string"); + goto fail; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + ModuleLog(i, BLOG_ERROR, "index is not a valid number (removing from list)"); + goto fail; + } + + if (index >= value_list_len(v)) { + ModuleLog(i, BLOG_ERROR, "index is out of bounds (removing from list)"); + goto fail; + } + + struct value *ov = value_list_at(v, index); + + value_list_remove(v, ov); + value_cleanup(ov); + } break; + + case NCDVAL_MAP: { + struct value *ov = value_map_find(v, where); + if (!ov) { + ModuleLog(i, BLOG_ERROR, "key does not exist (removing from map)"); + goto fail; + } + + value_map_remove(v, ov); + value_cleanup(ov); + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static int value_append (NCDModuleInst *i, struct value *v, NCDValRef data) +{ + ASSERT(v) + ASSERT((NCDVal_Type(data), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: { + if (!NCDVal_IsString(data)) { + ModuleLog(i, BLOG_ERROR, "cannot append non-string to string"); + return 0; + } + + size_t append_length = NCDVal_StringLength(data); + + if (append_length > SIZE_MAX - v->storedstring.length) { + ModuleLog(i, BLOG_ERROR, "too much data to append"); + return 0; + } + size_t new_length = v->storedstring.length + append_length; + + char *new_string = BRealloc(v->storedstring.data, new_length); + if (!new_string) { + ModuleLog(i, BLOG_ERROR, "BRealloc failed"); + return 0; + } + + NCDVal_StringCopyOut(data, 0, append_length, new_string + v->storedstring.length); + + v->storedstring.data = new_string; + v->storedstring.length = new_length; + } break; + + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + if (!NCDVal_IsString(data)) { + ModuleLog(i, BLOG_ERROR, "cannot append non-string to string"); + return 0; + } + + size_t length = value_string_length(v); + size_t append_length = NCDVal_StringLength(data); + + if (append_length > SIZE_MAX - length) { + ModuleLog(i, BLOG_ERROR, "too much data to append"); + return 0; + } + size_t new_length = length + append_length; + + char *new_string = BAlloc(new_length); + if (!new_string) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + return 0; + } + + value_string_copy_out(v, new_string); + NCDVal_StringCopyOut(data, 0, append_length, new_string + length); + + value_string_set_allocd(v, new_string, new_length); + } break; + + case NCDVAL_LIST: { + struct value *nv = value_init_fromvalue(i, data); + if (!nv) { + return 0; + } + + if (!value_list_insert(i, v, nv, value_list_len(v))) { + value_cleanup(nv); + return 0; + } + } break; + + default: + ModuleLog(i, BLOG_ERROR, "append is only defined for strings and lists"); + return 0; + } + + return 1; +} + +static void valref_init (struct valref *r, struct value *v) +{ + r->v = v; + + if (v) { + LinkedList0_Prepend(&v->refs_list, &r->refs_list_node); + } +} + +static void valref_free (struct valref *r) +{ + if (r->v) { + LinkedList0_Remove(&r->v->refs_list, &r->refs_list_node); + value_cleanup(r->v); + } +} + +static struct value * valref_val (struct valref *r) +{ + return r->v; +} + +static void valref_break (struct valref *r) +{ + ASSERT(r->v) + + LinkedList0_Remove(&r->v->refs_list, &r->refs_list_node); + r->v = NULL; +} + +static void func_new_common (void *vo, NCDModuleInst *i, struct value *v, value_deinit_func deinit_func, void *deinit_data) +{ + struct instance *o = vo; + o->i = i; + + // init value references + valref_init(&o->ref, v); + + // remember deinit + o->deinit_func = deinit_func; + o->deinit_data = deinit_data; + + NCDModuleInst_Backend_Up(i); + return; +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // deinit + if (o->deinit_func) { + o->deinit_func(o->deinit_data, o->i); + } + + // free value reference + valref_free(&o->ref); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + struct value *v = valref_val(&o->ref); + + if (name == ModuleString(o->i, STRING_EXISTS)) { + *out = ncd_make_boolean(mem, !!v, o->i->params->iparams->string_index); + return 1; + } + + if (name != NCD_STRING_TYPE && name != NCD_STRING_LENGTH && + name != ModuleString(o->i, STRING_KEYS) && name != NCD_STRING_EMPTY) { + return 0; + } + + if (!v) { + ModuleLog(o->i, BLOG_ERROR, "value was deleted"); + return 0; + } + + if (name == NCD_STRING_TYPE) { + *out = NCDVal_NewString(mem, get_type_str(v->type)); + } + else if (name == NCD_STRING_LENGTH) { + size_t len = 0; // to remove warning + switch (v->type) { + case NCDVAL_LIST: + len = value_list_len(v); + break; + case NCDVAL_MAP: + len = value_map_len(v); + break; + default: + ASSERT(value_is_string(v)) + len = value_string_length(v); + break; + } + + *out = ncd_make_uintmax(mem, len); + } + else if (name == ModuleString(o->i, STRING_KEYS)) { + if (v->type != NCDVAL_MAP) { + ModuleLog(o->i, BLOG_ERROR, "value is not a map (reading keys variable)"); + return 0; + } + + *out = NCDVal_NewList(mem, value_map_len(v)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (size_t j = 0; j < value_map_len(v); j++) { + struct value *ev = value_map_at(v, j); + + NCDValRef key = NCDVal_NewCopy(mem, ev->map_parent.key); + if (NCDVal_IsInvalid(key)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, key)) { + goto fail; + } + } + } + else if (name == NCD_STRING_EMPTY) { + if (!value_to_value(o->i, v, mem, out)) { + return 0; + } + } + else { + ASSERT(0); + } + + return 1; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void func_new_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_get (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get(i, mov, where_arg, 0); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_try_get (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get(i, mov, where_arg, 1); + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_getpath (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef path_arg; + if (!NCDVal_ListRead(params->args, 1, &path_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(path_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get_path(i, mov, path_arg); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert_replace_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_replace) +{ + NCDValRef where_arg = NCDVal_NewInvalid(); + NCDValRef what_arg; + if (!(!is_replace && NCDVal_ListRead(params->args, 1, &what_arg)) && + !NCDVal_ListRead(params->args, 2, &where_arg, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v; + + if (NCDVal_IsInvalid(where_arg)) { + v = value_insert_simple(i, mov, what_arg); + } else { + v = value_insert(i, mov, where_arg, what_arg, is_replace, NULL); + } + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_common(vo, i, params, 0); +} + +static void func_new_replace (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_common(vo, i, params, 1); +} + +struct insert_undo_deinit_data { + struct valref val_ref; + struct valref oldval_ref; +}; + +static void undo_deinit_func (struct insert_undo_deinit_data *data, NCDModuleInst *i) +{ + struct value *val = valref_val(&data->val_ref); + struct value *oldval = valref_val(&data->oldval_ref); + + if (val && val->parent && (!oldval || !oldval->parent)) { + // get parent + struct value *parent = val->parent; + + // remove this value from parent and restore saved one (or none) + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, val); + value_list_remove(parent, val); + if (oldval) { + int res = value_list_insert(i, parent, oldval, index); + ASSERT_EXECUTE(res) + } + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, val, &key_mem, &key); + if (oldval) { + int res = value_map_insert(parent, oldval, key_mem, key, i); + ASSERT_EXECUTE(res) + } else { + NCDValMem_Free(&key_mem); + } + } break; + + default: ASSERT(0); + } + } + + valref_free(&data->oldval_ref); + valref_free(&data->val_ref); + free(data); +} + +static void func_new_insert_replace_undo_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_replace) +{ + NCDValRef where_arg = NCDVal_NewInvalid(); + NCDValRef what_arg; + if (!(!is_replace && NCDVal_ListRead(params->args, 1, &what_arg)) && + !NCDVal_ListRead(params->args, 2, &where_arg, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct insert_undo_deinit_data *data = malloc(sizeof(*data)); + if (!data) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + struct value *oldv; + struct value *v; + + if (NCDVal_IsInvalid(where_arg)) { + oldv = NULL; + v = value_insert_simple(i, mov, what_arg); + } else { + v = value_insert(i, mov, where_arg, what_arg, is_replace, &oldv); + } + if (!v) { + goto fail1; + } + + valref_init(&data->val_ref, v); + valref_init(&data->oldval_ref, oldv); + + func_new_common(vo, i, v, (value_deinit_func)undo_deinit_func, data); + return; + +fail1: + free(data); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_undo_common(vo, i, params, 0); +} + +static void func_new_replace_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_undo_common(vo, i, params, 1); +} + +static void func_new_replace_this (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + if (mov->parent) { + struct value *parent = mov->parent; + + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, mov); + value_list_remove(parent, mov); + int res = value_list_insert(i, parent, v, index); + ASSERT_EXECUTE(res) + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, mov, &key_mem, &key); + int res = value_map_insert(parent, v, key_mem, key, i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + + value_cleanup(mov); + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_replace_this_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + struct insert_undo_deinit_data *data = malloc(sizeof(*data)); + if (!data) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail1; + } + + valref_init(&data->val_ref, v); + valref_init(&data->oldval_ref, mov); + + if (mov->parent) { + struct value *parent = mov->parent; + + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, mov); + value_list_remove(parent, mov); + int res = value_list_insert(i, parent, v, index); + ASSERT_EXECUTE(res) + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, mov, &key_mem, &key); + int res = value_map_insert(parent, v, key_mem, key, i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + } + + func_new_common(vo, i, v, (value_deinit_func)undo_deinit_func, data); + return; + +fail1: + value_cleanup(v); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_substr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef start_arg; + NCDValRef length_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &start_arg) && + !NCDVal_ListRead(params->args, 2, &start_arg, &length_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(start_arg) || (!NCDVal_IsInvalid(length_arg) && !NCDVal_IsString(length_arg))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t start; + if (!ncd_read_uintmax(start_arg, &start)) { + ModuleLog(i, BLOG_ERROR, "start is not a number"); + goto fail0; + } + + uintmax_t length = UINTMAX_MAX; + if (!NCDVal_IsInvalid(length_arg) && !ncd_read_uintmax(length_arg, &length)) { + ModuleLog(i, BLOG_ERROR, "length is not a number"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_is_string(mov)) { + ModuleLog(i, BLOG_ERROR, "value is not a string"); + goto fail0; + } + + size_t string_len = value_string_length(mov); + + if (start > string_len) { + ModuleLog(i, BLOG_ERROR, "start is out of range"); + goto fail0; + } + + size_t remain = string_len - start; + size_t amount = length < remain ? length : remain; + + struct value *v = NULL; + switch (mov->type) { + case STOREDSTRING_TYPE: { + v = value_init_storedstring(i, mov->storedstring.data + start, amount); + } break; + case IDSTRING_TYPE: { + v = value_init_storedstring(i, NCDStringIndex_Value(mov->idstring.string_index, mov->idstring.id) + start, amount); + } break; + case EXTERNALSTRING_TYPE: { + v = value_init_externalstring(i, mov->externalstring.data + start, amount, mov->externalstring.ref_target); + } break; + case COMPOSEDSTRING_TYPE: { + v = value_init_composedstring(i, mov->composedstring.resource, mov->composedstring.offset + start, amount); + } break; + default: + ASSERT(0); + } + + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void remove_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_remove(i, mov, where_arg)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void delete_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + value_delete(mov); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void reset_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef what_arg; + if (!NCDVal_ListRead(params->args, 1, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // build value from argument + struct value *newv = value_init_fromvalue(i, what_arg); + if (!newv) { + goto fail0; + } + + // deinit + if (mo->deinit_func) { + mo->deinit_func(mo->deinit_data, i); + } + + // free value reference + valref_free(&mo->ref); + + // set up value reference + valref_init(&mo->ref, newv); + + // set no deinit function + mo->deinit_func = NULL; + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void append_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_append(i, mov, data_arg)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "value", + .func_new2 = func_new_value, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::get", + .base_type = "value", + .func_new2 = func_new_get, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::try_get", + .base_type = "value", + .func_new2 = func_new_try_get, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::getpath", + .base_type = "value", + .func_new2 = func_new_getpath, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::insert", + .base_type = "value", + .func_new2 = func_new_insert, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace", + .base_type = "value", + .func_new2 = func_new_replace, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_this", + .base_type = "value", + .func_new2 = func_new_replace_this, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::insert_undo", + .base_type = "value", + .func_new2 = func_new_insert_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_undo", + .base_type = "value", + .func_new2 = func_new_replace_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_this_undo", + .base_type = "value", + .func_new2 = func_new_replace_this_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::remove", + .func_new2 = remove_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::delete", + .func_new2 = delete_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::reset", + .func_new2 = reset_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::substr", + .base_type = "value", + .func_new2 = func_new_substr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::append", + .func_new2 = append_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_value = { + .modules = modules, + .strings = strings +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/value_maptree.h b/app/src/main/jni/badvpn/ncd/modules/value_maptree.h new file mode 100644 index 0000000..e6f69f9 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/value_maptree.h @@ -0,0 +1,11 @@ +#define SAVL_PARAM_NAME MapTree +#define SAVL_PARAM_FEATURE_COUNTS 1 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct value +#define SAVL_PARAM_TYPE_KEY NCDValRef +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_TYPE_COUNT size_t +#define SAVL_PARAM_VALUE_COUNT_MAX SIZE_MAX +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) NCDVal_Compare((entry1)->map_parent.key, (entry2)->map_parent.key) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) NCDVal_Compare((key1), (entry2)->map_parent.key) +#define SAVL_PARAM_MEMBER_NODE map_parent.maptree_node diff --git a/app/src/main/jni/badvpn/ncd/modules/valuemetic.c b/app/src/main/jni/badvpn/ncd/modules/valuemetic.c new file mode 100644 index 0000000..a87f73b --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/valuemetic.c @@ -0,0 +1,219 @@ +/** + * @file valuemetic.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Comparison functions for values. + * + * Synopsis: + * val_lesser(v1, v2) + * val_greater(v1, v2) + * val_lesser_equal(v1, v2) + * val_greater_equal(v1, v2) + * val_equal(v1, v2) + * val_different(v1, v2) + * + * Variables: + * (empty) - "true" or "false", reflecting the value of the relation in question + * + * Description: + * These statements perform comparisons of values. Order of values is defined by the + * following rules: + * 1. Values of different types have the following order: strings, lists, maps. + * 2. String values are ordered lexicographically, with respect to the numeric values + * of their bytes. + * 3. List values are ordered lexicographically, where the order of the elements is + * defined by recursive application of these rules. + * 4. Map values are ordered lexicographically, as if a map was a list of (key, value) + * pairs ordered by key, where the order of both keys and values is defined by + * recursive application of these rules. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int result; +}; + +typedef int (*compute_func) (NCDValRef v1, NCDValRef v2); + +static int compute_lesser (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) < 0; +} + +static int compute_greater (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) > 0; +} + +static int compute_lesser_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) <= 0; +} + +static int compute_greater_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) >= 0; +} + +static int compute_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) == 0; +} + +static int compute_different (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) != 0; +} + +static void new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, compute_func cfunc) +{ + struct instance *o = vo; + o->i = i; + + NCDValRef v1_arg; + NCDValRef v2_arg; + if (!NCDVal_ListRead(params->args, 2, &v1_arg, &v2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + o->result = cfunc(v1_arg, v2_arg); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->result, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void func_new_lesser (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_lesser); +} + +static void func_new_greater (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_greater); +} + +static void func_new_lesser_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_lesser_equal); +} + +static void func_new_greater_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_greater_equal); +} + +static void func_new_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_equal); +} + +static void func_new_different (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_different); +} + +static struct NCDModule modules[] = { + { + .type = "val_lesser", + .func_new2 = func_new_lesser, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_greater", + .func_new2 = func_new_greater, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_lesser_equal", + .func_new2 = func_new_lesser_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_greater_equal", + .func_new2 = func_new_greater_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_equal", + .func_new2 = func_new_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_different", + .func_new2 = func_new_different, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_valuemetic = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/modules/var.c b/app/src/main/jni/badvpn/ncd/modules/var.c new file mode 100644 index 0000000..83abd44 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/modules/var.c @@ -0,0 +1,163 @@ +/** + * @file var.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Variable module. + * + * Synopsis: var(value) + * Variables: + * (empty) - value + * + * Synopsis: var::set(value) + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef value; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read argument + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // copy value + o->value = NCDVal_NewCopy(&o->mem, value_arg); + if (NCDVal_IsInvalid(o->value)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->value); + return 1; + } + + return 0; +} + +static void set_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // allocate new mem + NCDValMem mem; + NCDValMem_Init(&mem); + + // copy value + NCDValRef copy = NCDVal_NewCopy(&mem, value_arg); + if (NCDVal_IsInvalid(copy)) { + goto fail1; + } + + // replace value in var + NCDValMem_Free(&mo->mem); + mo->mem = mem; + mo->value = NCDVal_Moved(&mo->mem, copy); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDValMem_Free(&mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "var", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "var::set", + .func_new2 = set_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_var = { + .modules = modules +}; diff --git a/app/src/main/jni/badvpn/ncd/ncd.c b/app/src/main/jni/badvpn/ncd/ncd.c new file mode 100644 index 0000000..b3270fc --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/ncd.c @@ -0,0 +1,463 @@ +/** + * @file ncd.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_SYSLOG +#include +#endif + +#include "ncd.h" + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_STDERR 2 +#define LOGGER_SYSLOG 3 + +// command-line options +static struct { + int help; + int version; + int logger; +#ifdef BADVPN_USE_SYSLOG + char *logger_syslog_facility; + char *logger_syslog_ident; +#endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *config_file; + int syntax_only; + int retry_time; + int no_udev; + char **extra_args; + int num_extra_args; +} options; + +// reactor +static BReactor reactor; + +// process manager +static BProcessManager manager; + +// udev manager +static NCDUdevManager umanager; + +// random number generator +static BRandom2 random2; + +// interpreter +static NCDInterpreter interpreter; + +// forward declarations of functions +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static void signal_handler (void *unused); +static void interpreter_handler_finished (void *user, int exit_code); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + int main_exit_code = 1; + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + case LOGGER_STDERR: + BLog_InitStderr(); + break; +#ifdef BADVPN_USE_SYSLOG + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; +#endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } else { + BLog_SetChannelLoglevel(i, DEFAULT_LOGLEVEL); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // init process manager + if (!BProcessManager_Init(&manager, &reactor)) { + BLog(BLOG_ERROR, "BProcessManager_Init failed"); + goto fail2; + } + + // init udev manager + NCDUdevManager_Init(&umanager, options.no_udev, &reactor, &manager); + + // init random number generator + if (!BRandom2_Init(&random2, BRANDOM2_INIT_LAZY)) { + BLog(BLOG_ERROR, "BRandom2_Init failed"); + goto fail3; + } + + // setup signal handler + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail4; + } + + // build program + NCDProgram program; + if (!NCDBuildProgram_Build(options.config_file, &program)) { + BLog(BLOG_ERROR, "failed to build program"); + goto fail5; + } + + // setup interpreter parameters + struct NCDInterpreter_params params; + params.handler_finished = interpreter_handler_finished; + params.user = NULL; + params.retry_time = options.retry_time; + params.extra_args = options.extra_args; + params.num_extra_args = options.num_extra_args; + params.reactor = &reactor; + params.manager = &manager; + params.umanager = &umanager; + params.random2 = &random2; + + // initialize interpreter + if (!NCDInterpreter_Init(&interpreter, program, params)) { + goto fail5; + } + + // don't enter event loop if syntax check is requested + if (options.syntax_only) { + main_exit_code = 0; + goto fail6; + } + + BLog(BLOG_NOTICE, "entering event loop"); + + // enter event loop + main_exit_code = BReactor_Exec(&reactor); + +fail6: + // free interpreter + NCDInterpreter_Free(&interpreter); +fail5: + // remove signal handler + BSignal_Finish(); +fail4: + // free random number generator + BRandom2_Free(&random2); +fail3: + // free udev manager + NCDUdevManager_Free(&umanager); + + // free process manager + BProcessManager_Free(&manager); +fail2: + // free reactor + BReactor_Free(&reactor); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish objects + DebugObjectGlobal_Finish(); + + return main_exit_code; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger ]\n" + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--retry-time ]\n" + " [--no-udev]\n" + " [--config-file ]\n" + " [--syntax-only]\n" + " [-- program_args...]\n" + " [ program_args...]\n" , + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDERR; +#ifdef BADVPN_USE_SYSLOG + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; +#endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.config_file = NULL; + options.syntax_only = 0; + options.retry_time = DEFAULT_RETRY_TIME; + options.no_udev = 0; + options.extra_args = NULL; + options.num_extra_args = 0; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + else if (!strcmp(arg2, "stderr")) { + options.logger = LOGGER_STDERR; + } +#ifdef BADVPN_USE_SYSLOG + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } +#endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } +#ifdef BADVPN_USE_SYSLOG + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } +#endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--config-file")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.config_file = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syntax-only")) { + options.syntax_only = 1; + } + else if (!strcmp(arg, "--retry-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.retry_time = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--no-udev")) { + options.no_udev = 1; + } + else if (!strcmp(arg, "--")) { + options.extra_args = &argv[i + 1]; + options.num_extra_args = argc - i - 1; + i += options.num_extra_args; + } + else if (!string_begins_with(arg, "--")) { + if (options.config_file) { + fprintf(stderr, "%s: program is already specified (did you mean to use -- ?)\n", arg); + return 0; + } + options.config_file = argv[i]; + options.extra_args = &argv[i + 1]; + options.num_extra_args = argc - i - 1; + i += options.num_extra_args; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.config_file) { + fprintf(stderr, "No program is specified.\n"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + NCDInterpreter_RequestShutdown(&interpreter, 1); +} + +void interpreter_handler_finished (void *user, int exit_code) +{ + BReactor_Quit(&reactor, exit_code); +} diff --git a/app/src/main/jni/badvpn/ncd/ncd.h b/app/src/main/jni/badvpn/ncd/ncd.h new file mode 100644 index 0000000..88c4070 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/ncd.h @@ -0,0 +1,37 @@ +/** + * @file ncd.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "ncd" + +// how long to wait after an error before retrying +#define DEFAULT_RETRY_TIME 5000 + +// default loglevel +#define DEFAULT_LOGLEVEL BLOG_WARNING diff --git a/app/src/main/jni/badvpn/ncd/parse_linux_input.sh b/app/src/main/jni/badvpn/ncd/parse_linux_input.sh new file mode 100644 index 0000000..f277484 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/parse_linux_input.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +INPUT=$1 +OUTPUT=$2 + +types="" +keys="" +rels="" +abss="" +sws="" +mscs="" +leds="" +reps="" +snds="" +ffstatuss="" + +while read LINE; do + tab=$'\t' + space="[ ${tab}]" + regex="^#define ((EV|KEY|BTN|REL|ABS|SW|MSC|LED|REP|SND|FF_STATUS)_[A-Z0-9_]+)${space}" + if [[ $LINE =~ $regex ]]; then + type=${BASH_REMATCH[2]} + name=${BASH_REMATCH[1]} + if [[ $type = "EV" ]]; then + if [[ $name != "EV_VERSION" ]]; then + types="${types} [${name}] = \"${name}\", +" + fi + elif [[ $type = "KEY" ]] || [[ $type = "BTN" ]]; then + if [[ $name != "KEY_MIN_INTERESTING" ]]; then + keys="${keys} [${name}] = \"${name}\", +" + fi + elif [[ $type = "REL" ]]; then + rels="${rels} [${name}] = \"${name}\", +" + elif [[ $type = "ABS" ]]; then + abss="${abss} [${name}] = \"${name}\", +" + elif [[ $type = "SW" ]]; then + sws="${sws} [${name}] = \"${name}\", +" + elif [[ $type = "MSC" ]]; then + mscs="${mscs} [${name}] = \"${name}\", +" + elif [[ $type = "LED" ]]; then + leds="${leds} [${name}] = \"${name}\", +" + elif [[ $type = "REP" ]]; then + reps="${reps} [${name}] = \"${name}\", +" + elif [[ $type = "SND" ]]; then + snds="${snds} [${name}] = \"${name}\", +" + elif [[ $type = "FF_STATUS" ]]; then + ffstatuss="${ffstatuss} [${name}] = \"${name}\", +" + fi + fi +done < "${INPUT}" + +( +echo " +static const char *type_names[] = { +${types}}; + +static const char *key_names[] = { +${keys}}; + +static const char *rel_names[] = { +${rels}}; + +static const char *abs_names[] = { +${abss}}; + +static const char *sw_names[] = { +${sws}}; + +static const char *msc_names[] = { +${mscs}}; + +static const char *led_names[] = { +${leds}}; + +static const char *rep_names[] = { +${reps}}; + +static const char *snd_names[] = { +${snds}}; + +static const char *ffstatus_names[] = { +${ffstatuss}}; +" +) >"${OUTPUT}" diff --git a/app/src/main/jni/badvpn/ncd/static_strings.h b/app/src/main/jni/badvpn/ncd/static_strings.h new file mode 100644 index 0000000..a823224 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/static_strings.h @@ -0,0 +1,70 @@ +/** + * @file static_strings.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STATIC_STRINGS_H +#define BADVPN_STATIC_STRINGS_H + +// NOTE: keep synchronized with NCDStringIndex.c +enum { + NCD_STRING_EMPTY, + NCD_STRING_ARGS, + NCD_STRING_ARG0, + NCD_STRING_ARG1, + NCD_STRING_ARG2, + NCD_STRING_ARG3, + NCD_STRING_ARG4, + NCD_STRING_ARG5, + NCD_STRING_ARG6, + NCD_STRING_ARG7, + NCD_STRING_ARG8, + NCD_STRING_ARG9, + NCD_STRING_ARG10, + NCD_STRING_ARG11, + NCD_STRING_ARG12, + NCD_STRING_ARG13, + NCD_STRING_ARG14, + NCD_STRING_ARG15, + NCD_STRING_ARG16, + NCD_STRING_ARG17, + NCD_STRING_ARG18, + NCD_STRING_ARG19, + NCD_STRING_TRUE, + NCD_STRING_FALSE, + NCD_STRING_NONE, + NCD_STRING_CALLER, + NCD_STRING_SUCCEEDED, + NCD_STRING_IS_ERROR, + NCD_STRING_NOT_EOF, + NCD_STRING_LENGTH, + NCD_STRING_TYPE, + NCD_STRING_EXIT_STATUS, + NCD_STRING_SIZE +}; + +#endif diff --git a/app/src/main/jni/badvpn/ncd/tests/addr_in_network.ncd b/app/src/main/jni/badvpn/ncd/tests/addr_in_network.ncd new file mode 100644 index 0000000..fc53292 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/addr_in_network.ncd @@ -0,0 +1,60 @@ +process main { + net.ipv4.addr_in_network("192.168.6.0", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.0", "192.168.6.0/24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.1", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.255", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.5.255", "192.168.6.0", "24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("192.168.7.0", "192.168.6.0", "24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("192.168.7.0", "192.168.6.0/24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("0.0.0.0", "192.168.6.0", "0") r; + assert(r); + + net.ipv4.addr_in_network("0.0.0.0", "0.0.0.0", "0") r; + assert(r); + + net.ipv4.addr_in_network("255.255.255.255", "0.0.0.0", "0") r; + assert(r); + + net.ipv6.addr_in_network("::123:0", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:1", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:ffff", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:ffff", "::123:ffff/128") r; + assert(r); + + net.ipv6.addr_in_network("::122:ffff", "::123:0/112") r; + not(r) r; + assert(r); + + net.ipv6.addr_in_network("::124:0", "::123:0/112") r; + not(r) r; + assert(r); + + net.ipv6.addr_in_network("::123:fffe", "::123:ffff/128") r; + not(r) r; + assert(r); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/alias.ncd b/app/src/main/jni/badvpn/ncd/tests/alias.ncd new file mode 100644 index 0000000..624a4ed --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/alias.ncd @@ -0,0 +1,48 @@ +process foo { + var("hello") x; + alias("x") y; + val_equal(y, "hello") a; + assert(a); + + var("hello") x; + alias("x") y; + y->set("world"); + val_equal(y, "world") a; + assert(a); + + var("hello") x; + alias("x") y; + alias("y") z; + z->set("world"); + val_equal(x, "world") a; + assert(a); + + call("test", {"hello"}) c; + alias("c.x") x; + val_equal(x, "hello") a; + assert(a); + + call("test", {"hello"}) c; + alias("c") x; + alias("x") y; + alias("y.x") z; + c.x->set("world"); + val_equal(z, "world") a; + assert(a); + + var("hello") x; + call("test2", {"_caller.x"}) c; + c.x->set("world"); + val_equal(x, "world") a; + assert(a); + + exit("0"); +} + +template test { + var(_arg0) x; +} + +template test2 { + alias(_arg0) x; +} diff --git a/app/src/main/jni/badvpn/ncd/tests/arithmetic.ncd b/app/src/main/jni/badvpn/ncd/tests/arithmetic.ncd new file mode 100644 index 0000000..8f82bcc --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/arithmetic.ncd @@ -0,0 +1,69 @@ +process main { + num_lesser("6", "7") r; + assert(r); + + num_lesser("7", "7") r; + not(r) a; + assert(a); + + num_greater("7", "6") r; + assert(r); + + num_greater("7", "7") r; + not(r) a; + assert(a); + + num_lesser_equal("7", "7") r; + assert(r); + + num_lesser_equal("8", "7") r; + not(r) a; + assert(a); + + num_greater_equal("7", "7") r; + assert(r); + + num_greater_equal("7", "8") r; + not(r) a; + assert(a); + + num_equal("7", "7") r; + assert(r); + + num_equal("6", "7") r; + not(r) a; + assert(a); + + num_equal("7", "6") r; + not(r) a; + assert(a); + + num_different("7", "6") a; + assert(a); + + num_different("7", "007") a; + not(a) a; + assert(a); + + num_add("4", "7") r; + strcmp(r, "11") a; + assert(a); + + num_subtract("4", "3") r; + strcmp(r, "1") a; + assert(a); + + num_multiply("4", "5") r; + strcmp(r, "20") a; + assert(a); + + num_divide("7", "3") r; + strcmp(r, "2") a; + assert(a); + + num_modulo("7", "3") r; + strcmp(r, "1") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/backtracking.ncd b/app/src/main/jni/badvpn/ncd/tests/backtracking.ncd new file mode 100644 index 0000000..1faf3b7 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/backtracking.ncd @@ -0,0 +1,31 @@ +process main { + value({}) list; + var("0") i; + backtrack_point() point; + num_lesser(i, "100") do_more; + If (do_more) { + list->insert(i); + num_add(i, "1") new_i; + i->set(new_i); + point->go(); + }; + val_equal(list.length, "100") a; + assert(a); + + value({}) list; + var("0") i; + blocker() blk; + blk->up(); + blk->use(); + num_lesser(i, "100") do_more; + If (do_more) { + list->insert(i); + num_add(i, "1") new_i; + i->set(new_i); + blk->downup(); + }; + val_equal(list.length, "100") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/buffer.ncd b/app/src/main/jni/badvpn/ncd/tests/buffer.ncd new file mode 100644 index 0000000..1af0ea7 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/buffer.ncd @@ -0,0 +1,54 @@ +process main { + buffer() buf; + val_equal(buf, "") a; + assert(a); + + buf->append("12"); + val_equal(buf, "12") a; + assert(a); + + buf->append("345"); + val_equal(buf, "12345") a; + assert(a); + + buf->consume("1"); + val_equal(buf, "2345") a; + assert(a); + + buf->consume("1"); + val_equal(buf, "345") a; + assert(a); + + buf->consume("3"); + val_equal(buf, "") a; + assert(a); + + buf->append("6"); + val_equal(buf, "6") a; + assert(a); + + buf->append("7890"); + val_equal(buf, "67890") a; + assert(a); + + buf->append(""); + val_equal(buf, "67890") a; + assert(a); + + buf->consume("4"); + val_equal(buf, "0") a; + assert(a); + + buf->append("1234567890"); + val_equal(buf, "01234567890") a; + assert(a); + + val_equal(buf.length, "11") a; + assert(a); + + buffer("hello") buf2; + val_equal(buf2, "hello") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/call.ncd b/app/src/main/jni/badvpn/ncd/tests/call.ncd new file mode 100644 index 0000000..273ed68 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/call.ncd @@ -0,0 +1,18 @@ +process main { + var("bad_x") x; + var("good_x") y; + call("helper_func", {}) helper; + call_with_caller_target("func1", {}, "helper") c; + val_equal(c.x, "good_x") a; + assert(a); + + exit("0"); +} + +template helper_func { + var(_caller.y) x; +} + +template func1 { + var(_caller.x) x; +} diff --git a/app/src/main/jni/badvpn/ncd/tests/concat.ncd b/app/src/main/jni/badvpn/ncd/tests/concat.ncd new file mode 100644 index 0000000..72b256e --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/concat.ncd @@ -0,0 +1,19 @@ +process main { + concat("Hello", "", "World") x; + strcmp(x, "HelloWorld") a; + assert(a); + + concat("\x00\x00", "\x00") x; + strcmp(x, "\x00\x00\x00") a; + assert(a); + + concatv({"Hello", "", "World"}) x; + strcmp(x, "HelloWorld") a; + assert(a); + + concatv({"\x00\x00", "\x00"}) x; + strcmp(x, "\x00\x00\x00") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/depend.ncd b/app/src/main/jni/badvpn/ncd/tests/depend.ncd new file mode 100644 index 0000000..c1a34b6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/depend.ncd @@ -0,0 +1,64 @@ +process main { + var("hello") x; + provide("A"); + depend("A") d; + val_equal(d.x, "hello") a; + assert(a); + d.x->set("world"); + val_equal(d.x, "world") a; + assert(a); + + var("hello") x; + provide("B"); + val_equal(x, "world") a; + assert(a); + + var("hello") x; + provide("C"); + val_equal(x, "hello") a; + assert(a); + depend("C_done"); + val_equal(x, "world") a; + assert(a); + + var("hello") x; + blocker() blk; + provide("D"); + val_equal(x, "hello") a; + assert(a); + blk->up(); + val_equal(x, "0") a; + assert(a); + blk->down(); + blk->up(); + val_equal(x, "1") a; + assert(a); + + exit("0"); +} + +process proc1 { + depend("B") dep; + dep.x->set("world"); +} + +process proc2 { + depend("C") dep; + sleep("0", "0"); + dep.x->set("world"); + provide("C_done"); +} + +process proc3 { + depend("D") dep; + dep.blk->use(); + provide("E"); +} + +process proc4 { + var("0") i; + depend("E") dep; + dep.dep.x->set(i); + num_add(i, "1") j; + i->set(j); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/depend_scope.ncd b/app/src/main/jni/badvpn/ncd/tests/depend_scope.ncd new file mode 100644 index 0000000..a29a9a6 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/depend_scope.ncd @@ -0,0 +1,31 @@ +process main { + depend_scope() scope; + var("0") x; + process_manager() mgr; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "2") a; # must not have rebound temporarily to A during backtracking + assert(a); + exit("0"); + }; + + scope->provide("A"); + mgr->start("t1", "t1", {}); + val_equal(x, "1") a; # must have bound to A immediately + assert(a); + + scope->provide("B") mgr; + val_equal(x, "2") a; # must have rebound to B immediately + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template t1 { + _caller.scope->depend({"B", "A"}) dep; + num_add(dep.x, "1") new_x; + dep.x->set(new_x); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/escape_and_nulls.ncd b/app/src/main/jni/badvpn/ncd/tests/escape_and_nulls.ncd new file mode 100644 index 0000000..741bf8d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/escape_and_nulls.ncd @@ -0,0 +1,38 @@ +process main { + value("ab\0") str1; + value("ab") str2; + + strcmp(str1.length, "3") a; + assert(a); + + strcmp(str2.length, "2") a; + assert(a); + + strcmp(str1, str2) a; + not(a) a; + assert(a); + + concat(str1, str2) strc; + strcmp(strc, "ab\0ab") a; + assert(a); + + concat(str2, str1) strc; + strcmp(strc, "abab\0") a; + assert(a); + + value("") str1; + value("\x00\x00") str2; + value("\x00\x01") str3; + value("\x01") str4; + + val_lesser(str1, str2) a; + assert(a); + + val_lesser(str2, str3) a; + assert(a); + + val_lesser(str3, str4) a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/explode.ncd b/app/src/main/jni/badvpn/ncd/tests/explode.ncd new file mode 100644 index 0000000..20913a8 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/explode.ncd @@ -0,0 +1,23 @@ +process main { + explode("FOO", "aaaFOObbbFOOcccFOOddd") l; + val_equal(l, {"aaa", "bbb", "ccc", "ddd"}) a; + assert(a); + + explode("FOO", "FOObbbFOOFOO") l; + val_equal(l, {"", "bbb", "", ""}) a; + assert(a); + + explode("FOO", "foo") l; + val_equal(l, {"foo"}) a; + assert(a); + + explode("FOO", "FOO") l; + val_equal(l, {"", ""}) a; + assert(a); + + explode("participate in parachute", "parachute in participation of participate in parachuteparparticipate in parachute participate in parachut") l; + val_equal(l, {"parachute in participation of ", "par", " participate in parachut"}) a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/foreach.ncd b/app/src/main/jni/badvpn/ncd/tests/foreach.ncd new file mode 100644 index 0000000..33ad042 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/foreach.ncd @@ -0,0 +1,35 @@ +process main { + var({"a", "b", "c", "d"}) list; + value(["a":"1", "b":"2", "c":"3", "d":"4"]) map; + + value({}) new; + Foreach (list As value) { + new->insert(new.length, value); + }; + val_equal(new, list) a; + assert(a); + + value({}) new; + Foreach (list As index:value) { + new->insert(index, value); + }; + val_equal(new, list) a; + assert(a); + + value([]) new; + Foreach (map As key) { + map->get(key) value; + new->insert(key, value); + }; + val_equal(new, map) a; + assert(a); + + value([]) new; + Foreach (map As key:value) { + new->insert(key, value); + }; + val_equal(new, map) a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/if.ncd b/app/src/main/jni/badvpn/ncd/tests/if.ncd new file mode 100644 index 0000000..4597adc --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/if.ncd @@ -0,0 +1,38 @@ +process foo { + If ("true") { + If ("truee") { + var("A1") y; + } else { + If ("true") { + var("A11") q; + } else { + var("A22") q; + } t; + var(t.q) y; + } s; + var(s.y) x; + } elif ("true") { + var("B") x; + } else { + var("C") x; + } ifs; + + val_equal(ifs.x, "A11") a; + assert(a); + + var("a") v; + If ("false") { + v->set("b"); + }; + val_equal(v, "a") a; + assert(a); + + var("a") v; + If ("true") { + v->set("b"); + }; + val_equal(v, "b") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/implode.ncd b/app/src/main/jni/badvpn/ncd/tests/implode.ncd new file mode 100644 index 0000000..2197c17 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/implode.ncd @@ -0,0 +1,15 @@ +process main { + implode("X", {"a", "bb", "", "c"}) str; + strcmp(str, "aXbbXXc") a; + assert(a); + + implode("", {"a", "b"}) str; + strcmp(str, "ab") a; + assert(a); + + implode("X", {}) str; + strcmp(str, "") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/include.ncd b/app/src/main/jni/badvpn/ncd/tests/include.ncd new file mode 100644 index 0000000..eccb76d --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/include.ncd @@ -0,0 +1,16 @@ +include "include_included.ncdi" +include "include_included.ncdi" +include "include_included2.ncdi" +include "include_included2.ncdi" + +process main { + call("incl_tmpl", {}) c; + val_equal(c.x, "good") a; + assert(a); + + call("incl_tmpl2", {}) c; + val_equal(c.x, "good2") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/include_included.ncdi b/app/src/main/jni/badvpn/ncd/tests/include_included.ncdi new file mode 100644 index 0000000..5fbfd62 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/include_included.ncdi @@ -0,0 +1,5 @@ +include_guard "include_included" + +template incl_tmpl { + var("good") x; +} diff --git a/app/src/main/jni/badvpn/ncd/tests/include_included2.ncdi b/app/src/main/jni/badvpn/ncd/tests/include_included2.ncdi new file mode 100644 index 0000000..d765301 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/include_included2.ncdi @@ -0,0 +1,5 @@ +include_guard "include_included2" + +template incl_tmpl2 { + var("good2") x; +} diff --git a/app/src/main/jni/badvpn/ncd/tests/logical.ncd b/app/src/main/jni/badvpn/ncd/tests/logical.ncd new file mode 100644 index 0000000..e8956ec --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/logical.ncd @@ -0,0 +1,46 @@ +process main { + var("true") t; + var("Faalse") f; + + and(t, f) r; + strcmp(r, "false") a; + assert(a); + + and(f, t) r; + strcmp(r, "false") a; + assert(a); + + and(f, f) r; + strcmp(r, "false") a; + assert(a); + + and(t, t) r; + strcmp(r, "true") a; + assert(a); + + or(t, f) r; + strcmp(r, "true") a; + assert(a); + + or(f, t) r; + strcmp(r, "true") a; + assert(a); + + or(t, t) r; + strcmp(r, "true") a; + assert(a); + + or(f, f) r; + strcmp(r, "false") a; + assert(a); + + not(f) r; + strcmp(r, "true") a; + assert(a); + + not(t) r; + strcmp(r, "false") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/multidepend.ncd b/app/src/main/jni/badvpn/ncd/tests/multidepend.ncd new file mode 100644 index 0000000..b641f39 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/multidepend.ncd @@ -0,0 +1,30 @@ +process main { + var("0") x; + process_manager() mgr; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "2") a; # must not have rebound temporarily to A during backtracking + assert(a); + exit("0"); + }; + + multiprovide("A"); + mgr->start("t1", "t1", {}); + val_equal(x, "1") a; # must have bound to A immediately + assert(a); + + multiprovide("B") mgr; + val_equal(x, "2") a; # must have rebound to B immediately + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template t1 { + multidepend({"B", "A"}) dep; + num_add(dep.x, "1") new_x; + dep.x->set(new_x); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/netmask.ncd b/app/src/main/jni/badvpn/ncd/tests/netmask.ncd new file mode 100644 index 0000000..90cd744 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/netmask.ncd @@ -0,0 +1,15 @@ +process main { + ipv4_prefix_to_mask("16") mask; + strcmp(mask, "255.255.0.0") a; + assert(a); + + ipv4_mask_to_prefix("128.0.0.0") prefix; + strcmp(prefix, "1") a; + assert(a); + + ipv4_net_from_addr_and_prefix("192.168.1.4", "24") net; + strcmp(net, "192.168.1.0") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/parse.ncd b/app/src/main/jni/badvpn/ncd/tests/parse.ncd new file mode 100644 index 0000000..ae4820c --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/parse.ncd @@ -0,0 +1,85 @@ +process main { + parse_number("awfa") x; + not(x.succeeded) a; + assert(a); + + parse_number("023182") x; + assert(x.succeeded); + val_equal(x, "23182") a; + assert(a); + + parse_ipv4_addr("192.168.61.007") x; + assert(x.succeeded); + val_equal(x, "192.168.61.7") a; + assert(a); + + parse_ipv6_addr("1234:0000::abcd") x; + assert(x.succeeded); + val_equal(x, "1234::abcd") a; + assert(a); + + parse_value("{\"Hello World\", {}}") x; + assert(x.succeeded); + val_equal(x, {"Hello World", {}}) a; + assert(a); + + var({"Hello", "fw", {}, {}, ["key":{{}}, [[]:[]]:["k":"v"]], {"st", {"ri", {"ng", [[{}:{}]:[]]}}}}) v; + to_string(v) str; + from_string(str) v2; + to_string(v2) str2; + val_equal(v, v2) a; + assert(a); + val_equal(str, str2) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}") x; + assert(x.succeeded); + + parse_value("{\"Hello\", \"fw\", {}, {}, \"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st, {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", \"ri\", \"ng\", [[{}:{}]:[]]}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{syntax error") x; + not(x.succeeded) a; + assert(a); + + parse_ipv4_cidr_addr("192.168.61.007/24") x; + assert(x.succeeded); + val_equal(x, "192.168.61.7/24") a; + assert(a); + val_equal(x.addr, "192.168.61.7") a; + assert(a); + val_equal(x.prefix, "24") a; + assert(a); + + parse_ipv4_cidr_addr("192.168.61.007/33") x; + not(x.succeeded) a; + assert(a); + + parse_ipv6_cidr_addr("1234:0000::abcd/41") x; + assert(x.succeeded); + val_equal(x, "1234::abcd/41") a; + assert(a); + val_equal(x.addr, "1234::abcd") a; + assert(a); + val_equal(x.prefix, "41") a; + assert(a); + + parse_ipv6_cidr_addr("1234:0000::abcd/129") x; + not(x.succeeded) a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/process_manager.ncd b/app/src/main/jni/badvpn/ncd/tests/process_manager.ncd new file mode 100644 index 0000000..98f0819 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/process_manager.ncd @@ -0,0 +1,112 @@ +process main { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + call("phase2", {}); + }; + + process_manager() mgr; + + mgr->start("name", "increment", {"1", "2", "false"}); + val_equal(x, "1") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "3") a; + assert(a); + + mgr->start("name", "increment", {"3", "4", "true"}); + val_equal(x, "6") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "6") a; + assert(a); + + mgr->start("name", "increment", {"5", "6", "false"}); + val_equal(x, "6") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template phase2 { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + call("phase3", {}); + }; + + process_manager() mgr; + + mgr->start("name", "increment", {"1", "2", "true"}); + val_equal(x, "1") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "1") a; + assert(a); + + mgr->start("name", "increment", {"3", "4", "true"}); + val_equal(x, "1") a; + assert(a); + + depend("INC_DONE"); + val_equal(x, "6") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template phase3 { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + exit("0"); + }; + + process_manager() mgr; + + mgr->start("increment", {"1", "2", "false"}); + val_equal(x, "1") a; + assert(a); + + mgr->start("increment", {"3", "4", "false"}); + val_equal(x, "4") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template increment { + var(_arg0) amount; + var(_arg1) amount_deinit; + var(_arg2) do_sleep; + imperative("", {}, "increment_deinit_inc", {}, "10000"); + num_add(_caller.x, amount) new_x; + _caller.x->set(new_x); + If (do_sleep) { + sleep("0", "0"); + }; + provide("INC_DONE"); +} + +template increment_deinit_inc { + num_add(_caller._caller.x, _caller.amount_deinit) new_x; + _caller._caller.x->set(new_x); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/regex.ncd b/app/src/main/jni/badvpn/ncd/tests/regex.ncd new file mode 100644 index 0000000..3175ebe --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/regex.ncd @@ -0,0 +1,48 @@ +process main { + var("FOO BAR BAZ QUX goo") x; + regex_replace(x, {"FOO", "BAR", "goo"}, {"BAR", "bar", "GOO"}) y; + strcmp(y, "BAR bar BAZ QUX GOO") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"^hello"}, {"Hello,"}) y; + strcmp(y, "Hello, world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"goodbye"}, {"hello"}) y; + strcmp(y, "hello world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"hello world"}, {"hello NCD"}) y; + strcmp(y, "hello NCD") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"wor"}, {"Wor"}) y; + strcmp(y, "hello World") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "llo"}, {"ELL", "LLO"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "el"}, {"ELL", "EL"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"el", "lo"}, {"EL", "LO"}) y; + strcmp(y, "hELLO world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "ll"}, {"ELL", "LL"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/run_tests b/app/src/main/jni/badvpn/ncd/tests/run_tests new file mode 100644 index 0000000..071fd20 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/run_tests @@ -0,0 +1,38 @@ +#!/bin/bash + +NCD=$1 +USE_VALGRIND=$2 + +if [[ -z $NCD ]] || [[ -n $USE_VALGRIND && $USE_VALGRIND != use_valgrind ]]; then + echo "Usage: $0 [use_valgrind]" + exit 1 +fi + +if [[ ! -e ./run_tests ]]; then + echo "Must run from the tests directory" + exit 1 +fi + +failed=0 + +for file in ./*.ncd; do + echo "Running: $file" + if [[ $USE_VALGRIND = use_valgrind ]]; then + valgrind --error-exitcode=1 --leak-check=full "$NCD" --loglevel none --config-file "$file" + else + "$NCD" --loglevel none --config-file "$file" + fi + res=$? + if [[ ! $res -eq 0 ]]; then + echo "FAILED" + let failed+=1 + fi +done + +if [[ $failed -gt 0 ]]; then + echo "$failed tests FAILED" + exit 1 +fi + +echo "all tests passed" +exit 0 diff --git a/app/src/main/jni/badvpn/ncd/tests/strings.ncd b/app/src/main/jni/badvpn/ncd/tests/strings.ncd new file mode 100644 index 0000000..7be031b --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/strings.ncd @@ -0,0 +1,47 @@ +process main { + buffer() buf; + buf->append("12"); + buf->append("345"); + buf->append("6"); + num_equal(buf, "123456") a; + assert(a); + + var("false") check; + call("test_func", {}); + assert(check); + + buffer() buf; + buf->append("test_func"); + var("false") check; + call(buf, {}); + assert(check); + + concat("test_func") cnc; + var("false") check; + call(cnc, {}); + assert(check); + + buffer() buf; + buf->append("test_func"); + var("false") check; + process_manager() mgr; + mgr->start(buf, {}); + assert(check); + + buffer() buf; + buf->append("/bin/echo"); + runonce({buf, buf}); + + buffer() buf; + buf->append("12"); + buf->append("345"); + to_string(buf) str; + val_equal(str, "\"12345\"") a; + assert(a); + + exit("0"); +} + +template test_func { + _caller.check->set("true"); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/substr.ncd b/app/src/main/jni/badvpn/ncd/tests/substr.ncd new file mode 100644 index 0000000..a3b07b1 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/substr.ncd @@ -0,0 +1,37 @@ +process main { + var("0123456789") str; + concat(str) external_str; + + call("do_test", {"_caller.str"}); + call("do_test", {"_caller.external_str"}); + + exit("0"); +} + +template do_test { + alias(_arg0) str; + + substr(str, "0") sub; + val_equal(sub, "0123456789") a; + assert(a); + + substr(str, "2") sub; + val_equal(sub, "23456789") a; + assert(a); + + substr(str, "3", "0") sub; + val_equal(sub, "") a; + assert(a); + + substr(str, "3", "6") sub; + val_equal(sub, "345678") a; + assert(a); + + substr(str, "3", "7") sub; + val_equal(sub, "3456789") a; + assert(a); + + substr(str, "3", "8") sub; + val_equal(sub, "3456789") a; + assert(a); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/turing.ncd b/app/src/main/jni/badvpn/ncd/tests/turing.ncd new file mode 100644 index 0000000..4e764f1 --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/turing.ncd @@ -0,0 +1,138 @@ +process main { + # Turing machine specification. + var("B") blank; + var([ + {"0", "0"}:{"0", "0", "right"}, + {"0", "1"}:{"1", "x", "right"}, + {"1", "1"}:{"1", "1", "right"}, + {"1", "0"}:{"2", "0", "right"}, + {"2", "0"}:{"2", "0", "right"}, + {"2", "1"}:{"3", "1", "right"}, + {"3", "1"}:{"3", "1", "right"}, + {"3", "0"}:{"4", "1", "left"}, + {"3", "B"}:{"4", "1", "left"}, + {"4", "1"}:{"4", "1", "left"}, + {"4", "0"}:{"5", "0", "left"}, + {"5", "0"}:{"5", "0", "left"}, + {"5", "1"}:{"6", "1", "left"}, + {"5", "x"}:{"h", "x", "stay"}, + {"6", "1"}:{"6", "1", "left"}, + {"6", "x"}:{"0", "x", "right"}, + {"6", "0"}:{"0", "0", "right"} + ]) rules; + var("0") initial_state; + var({}) initial_tape_left; + var({ + "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", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "0", "0", + "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", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1" + }) initial_tape_right; + + # Perform the computation, stopping when no rule matches. + call("turing", {blank, rules, initial_state, initial_tape_left, initial_tape_right}) results; + + # Check results. + + val_equal(results.tape_left, {"B"}) a; + assert(a); + + val_equal(results.tape_right, + {"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", + "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", + "0", "0", "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", "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", "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", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1"} + ) a; + assert(a); + + val_equal({results.side, results.pos}, {"right", "55"}) a; + assert(a); + + val_equal(results.state, "h") a; + assert(a); + + exit("0"); +} + +template turing { + alias("_arg0") blank; + value(_arg1) rules; + alias("_arg2") initial_state; + alias("_arg3") initial_tape_left; + alias("_arg4") initial_tape_right; + + # Head state. + var(initial_state) state; + + # Tape. Positions go like this: ... L2 L1 L0 R0 R1 R2 ... + value(initial_tape_left) tape_left; + value(initial_tape_right) tape_right; + + # Make sure each side of the tape has at least one symbol so we can flip easily. + tape_left->insert(tape_left.length, blank); + tape_right->insert(tape_right.length, blank); + + # Head position. + var("right") side; + var("0") pos; + + # Enter loop. + blocker() loop_blk; + loop_blk->up(); + loop_blk->use(); + + # Get symbol under head. + concat("tape_", side) tape_name; + alias(tape_name) cur_tape; + cur_tape->get(pos) symbol; + + # Look for a matching rule. + rules->try_get({state, symbol}) rule; + + If (rule.exists) { + # Extract directions from rule. + rule->get("0") new_state; + rule->get("1") new_symbol; + rule->get("2") move; + + # Change head state. + state->set(new_state); + + # Replace symbol under head. + cur_tape->remove(pos); + cur_tape->insert(pos, new_symbol); + + # Branch based on how we move. + strcmp(move, side) is_outside; + strcmp(move, "stay") is_stay; + strcmp(pos, "0") is_zero; + If (is_outside) { + # Increment position. + num_add(pos, "1") new_pos; + pos->set(new_pos); + + # If the new position is out of range, extend tape. + strcmp(pos, cur_tape.length) need_extend; + If (need_extend) { + cur_tape->insert(pos, blank); + }; + } elif (is_stay) { + # Nop. + getargs(); + } elif (is_zero) { + # Flip side, leave pos at zero. + side->set(move); + } else { + # Decrement position. + num_subtract(pos, "1") new_pos; + pos->set(new_pos); + }; + + # Continue loop. + loop_blk->downup(); + }; +} diff --git a/app/src/main/jni/badvpn/ncd/tests/value.ncd b/app/src/main/jni/badvpn/ncd/tests/value.ncd new file mode 100644 index 0000000..e483b3f --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/value.ncd @@ -0,0 +1,258 @@ +process main { + value({"A", {"B", "C"}, {{"D"}, "E"}}) v; + val_equal(v, {"A", {"B", "C"}, {{"D"}, "E"}}) a; + assert(a); + + v->get("1") w; + val_equal(w, {"B", "C"}) a; + assert(a); + + w->delete(); + val_equal(v, {"A", {{"D"}, "E"}}) a; + assert(a); + + v->getpath({"1", "1"}) f; + val_equal(f, "E") a; + assert(a); + + value(["hello":{"Hello", "Good evening!"}, "goodbye":{"Bye", "See you"}]) v; + val_equal(v, ["hello":{"Hello", "Good evening!"}, "goodbye":{"Bye", "See you"}]) a; + assert(a); + + val_equal(v.keys, {"goodbye", "hello"}) a; + assert(a); + + v->get("hello") h; + val_equal(h, {"Hello", "Good evening!"}) a; + assert(a); + + v->get("goodbye") g; + val_equal(g, {"Bye", "See you"}) a; + assert(a); + + g->delete(); + val_equal(v, ["hello":{"Hello", "Good evening!"}]) a; + assert(a); + + h->delete(); + val_equal(v, []); + assert(a); + + v->delete(); + strcmp(v.exists, "false") a; + assert(a); + + value({"D", "F", "H"}) v; + v->insert("0", "A") a; # ADFH + v->insert("1", "B") b; # ABDFH + v->insert("5", "I") i; # ABDFHI + val_equal(v, {"A", "B", "D", "F", "H", "I"}) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->insert("k0", "v0") v0; # k0=v0 k1=v1 k2=v2 k3=v3 + v->insert("k0", "V0") V0; # k0=V0 k1=v1 k2=v2 k3=v3 + val_equal(v0, "v0") a; + assert(a); + val_equal(V0, "V0") a; + assert(a); + + value({"D", "F", "H"}) v; + v->remove("0"); # FH + v->remove("1"); # F + val_equal(v, {"F"}) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->remove("k1"); + v->remove("k3"); + val_equal(v, ["k2":"v2"]) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->try_get("k1") v1; + v->try_get("k7") v7; + val_equal(v1.exists, "true") a; + assert(a); + val_equal(v7.exists, "false") a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + imperative("", {}, "check1", {}, "10000"); + v->insert_undo("k1", "V1") V1; + strcmp(V1, "V1") a; + assert(a); + v->insert_undo("k4", "V4"); + v->remove("k2"); + val_equal(v, ["k1":"V1", "k3":"v3", "k4":"V4"]) a; + assert(a); + + value({"a", "b", "c"}) v; + v->replace("0", "A"); + v->replace("1", "B"); + v->replace("2", "C"); + v->replace("3", "D"); + val_equal(v, {"A", "B", "C", "D"}) a; + assert(a); + + value({"a", "b", "c"}) v; + imperative("", {}, "check2", {}, "10000"); + v->replace_undo("0", "A"); + v->replace_undo("1", "B"); + v->replace_undo("2", "C"); + v->replace_undo("3", "D"); + val_equal(v, {"A", "B", "C", "D"}) a; + assert(a); + + value("A") v; + v->reset("B"); + val_equal(v, "B") a; + assert(a); + + value({"a", "c"}) v; + v->insert_undo("1", "b") vb; + val_equal(v, {"a", "b", "c"}) a; + assert(a); + val_equal(vb, "b") a; + assert(a); + vb->reset("B"); + val_equal(v, {"a", "c"}) a; + assert(a); + val_equal(vb, "B") a; + assert(a); + + value({"a", "b", "c"}) v; + v->get("1") vb; + vb->replace_this("B") vB; + val_equal(vb, "b") a; + assert(a); + val_equal(vB, "B") a; + assert(a); + v->get("1") vB2; + val_equal(vB2, "B") a; + assert(a); + + value(["a":"va", "b":"vb", "c":"vc"]) v; + v->get("b") vb; + vb->replace_this("vB") vB; + val_equal(vB, "vB") a; + assert(a); + val_equal(vb, "vb") a; + assert(a); + v->get("b") vB2; + val_equal(vB2, "vB") a; + assert(a); + + value({"a", "b", "c"}) v; + v->get("1") vb; + imperative("", {}, "check3", {}, "10000"); + vb->replace_this_undo("B") vB; + val_equal(vb, "b") a; + assert(a); + val_equal(vB, "B") a; + assert(a); + v->get("1") vB2; + val_equal(vB2, "B") a; + assert(a); + + value(["a":"va", "b":"vb", "c":"vc"]) v; + v->get("b") vb; + imperative("", {}, "check4", {}, "10000"); + vb->replace_this_undo("vB") vB; + val_equal(vB, "vB") a; + assert(a); + val_equal(vb, "vb") a; + assert(a); + v->get("b") vB2; + val_equal(vB2, "vB") a; + assert(a); + + value("") v; + v->append("ab"); + v->append("cde"); + v->append(v); + v->append(""); + v->append("f"); + val_equal(v, "abcdeabcdef") a; + assert(a); + + value({}) v; + v->insert("1") ins_v; + v->insert("2"); + v->insert("3"); + v->insert("4"); + v->append("5"); + v->append("6"); + val_equal(v, {"1", "2", "3", "4", "5", "6"}) a; + assert(a); + val_equal(ins_v, "1") a; + assert(a); + + value({}) v; + imperative("", {}, "check5", {}, "10000"); + v->insert_undo("1"); + v->insert_undo("2"); + v->insert_undo("3"); + v->insert_undo("4"); + val_equal(v, {"1", "2", "3", "4"}) a; + assert(a); + + buffer() buf; + buf->append("123"); + value(buf) v; + val_equal(v, "123") a; + assert(a); + v->substr("2") sub_v; + val_equal(sub_v, "3") a; + assert(a); + v->append("456789012345"); + val_equal(v, "123456789012345") a; + assert(a); + buffer() numbuf; + numbuf->append("1"); + numbuf->append("2"); + v->substr(numbuf) sub_v; + val_equal(sub_v, "345") a; + assert(a); + + concat("hello", "world") cnc; + value(cnc) v; + val_equal(v, "helloworld") a; + assert(a); + v->substr("2") sub_v; + val_equal(sub_v, "lloworld") a; + assert(a); + v->append("!!"); + val_equal(v, "helloworld!!") a; + assert(a); + v->substr("1") sub_v; + val_equal(sub_v, "elloworld!!") a; + assert(a); + + exit("0"); +} + +template check1 { + val_equal(_caller.v, ["k1":"v1", "k3":"v3"]) a; + assert(a); +} + +template check2 { + val_equal(_caller.v, {"a", "b", "c"}) a; + assert(a); +} + +template check3 { + val_equal(_caller.v, {"a", "b", "c"}) a; + assert(a); +} + +template check4 { + val_equal(_caller.v, ["a":"va", "b":"vb", "c":"vc"]) a; + assert(a); +} + +template check5 { + val_equal(_caller.v, {}) a; + assert(a); +} diff --git a/app/src/main/jni/badvpn/ncd/tests/value_substr.ncd b/app/src/main/jni/badvpn/ncd/tests/value_substr.ncd new file mode 100644 index 0000000..ea46e8a --- /dev/null +++ b/app/src/main/jni/badvpn/ncd/tests/value_substr.ncd @@ -0,0 +1,25 @@ +process foo { + value("0123456789") str; + + str->substr("0") sub; + strcmp(sub, str) a; + assert(a); + + str->substr("1") sub; + strcmp(sub, "123456789") a; + assert(a); + + str->substr("1", "0") sub; + strcmp(sub, "") a; + assert(a); + + str->substr("1", "9") sub; + strcmp(sub, "123456789") a; + assert(a); + + str->substr("1", "8") sub; + strcmp(sub, "12345678") a; + assert(a); + + exit("0"); +} diff --git a/app/src/main/jni/badvpn/nspr_support/BSSLConnection.c b/app/src/main/jni/badvpn/nspr_support/BSSLConnection.c new file mode 100644 index 0000000..c9d5000 --- /dev/null +++ b/app/src/main/jni/badvpn/nspr_support/BSSLConnection.c @@ -0,0 +1,1024 @@ +/** + * @file BSSLConnection.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include +#include + +#include "BSSLConnection.h" + +#include + +#define THREADWORK_STATE_NONE 0 +#define THREADWORK_STATE_HANDSHAKE 1 +#define THREADWORK_STATE_READ 2 +#define THREADWORK_STATE_WRITE 3 + +static void backend_threadwork_start (struct BSSLConnection_backend *b, int op); +static int backend_threadwork_do_io (struct BSSLConnection_backend *b); +static void connection_init_job_handler (BSSLConnection *o); +static void connection_init_up (BSSLConnection *o); +static void connection_try_io (BSSLConnection *o); +static void connection_threadwork_func_work (void *user); +static void connection_threadwork_handler_done (void *user); +static void connection_recv_job_handler (BSSLConnection *o); +static void connection_try_handshake (BSSLConnection *o); +static void connection_try_send (BSSLConnection *o); +static void connection_try_recv (BSSLConnection *o); +static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len); +static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len); + +int bprconnection_initialized = 0; +PRDescIdentity bprconnection_identity; + +static PRFileDesc * get_bottom (PRFileDesc *layer) +{ + while (layer->lower) { + layer = layer->lower; + } + + return layer; +} + +static PRStatus method_close (PRFileDesc *fd) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(!b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + + // free mutexes + if ((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + BMutex_Free(&b->recv_buf_mutex); + BMutex_Free(&b->send_buf_mutex); + } + + // free backend + free(b); + + // set no secret + fd->secret = NULL; + + return PR_SUCCESS; +} + +static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(amount > 0) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->recv_buf_mutex); + } + + // if we are receiving into buffer or buffer has no data left, refuse recv + if (b->recv_busy || b->recv_pos == b->recv_len) { + if (b->threadwork_state != THREADWORK_STATE_NONE) { + b->threadwork_want_recv = 1; + BMutex_Unlock(&b->recv_buf_mutex); + } else { + // start receiving if not already + if (!b->recv_busy) { + // set recv busy + b->recv_busy = 1; + + // receive into buffer + StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); + } + } + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + // limit amount to available data + if (amount > b->recv_len - b->recv_pos) { + amount = b->recv_len - b->recv_pos; + } + + // copy data + memcpy(buf, b->recv_buf + b->recv_pos, amount); + + // update buffer + b->recv_pos += amount; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->recv_buf_mutex); + } + + return amount; +} + +static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(amount > 0) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->send_buf_mutex); + } + + ASSERT(!b->send_busy || b->send_pos < b->send_len) + + // if there is data in buffer, refuse send + if (b->send_pos < b->send_len) { + if (b->threadwork_state != THREADWORK_STATE_NONE) { + b->threadwork_want_send = 1; + BMutex_Unlock(&b->send_buf_mutex); + } + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + // limit amount to buffer size + if (amount > BSSLCONNECTION_BUF_SIZE) { + amount = BSSLCONNECTION_BUF_SIZE; + } + + // init buffer + memcpy(b->send_buf, buf, amount); + b->send_pos = 0; + b->send_len = amount; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } else { + // start sending + b->send_busy = 1; + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + } + + return amount; +} + +static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + ASSERT(flags == 0) + + return method_read(fd, buf, amount); +} + +static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + ASSERT(flags == 0) + + return method_write(fd, buf, amount); +} + +static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + return in_flags; +} + +static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + memset(addr, 0, sizeof(*addr)); + addr->raw.family = PR_AF_INET; + return PR_SUCCESS; +} + +static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data) +{ + switch (data->option) { + case PR_SockOpt_Nonblocking: + data->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + } + + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data) +{ + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRIntn _PR_InvalidIntn (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt32 _PR_InvalidInt32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt64 _PR_InvalidInt64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset32 _PR_InvalidOffset32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset64 _PR_InvalidOffset64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRStatus _PR_InvalidStatus (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc *_PR_InvalidDesc (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +static PRIOMethods methods = { + (PRDescType)0, + method_close, + method_read, + method_write, + (PRAvailableFN)_PR_InvalidInt32, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidOffset32, + (PRSeek64FN)_PR_InvalidOffset64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt32, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + method_shutdown, + method_recv, + method_send, + (PRRecvfromFN)_PR_InvalidInt32, + (PRSendtoFN)_PR_InvalidInt32, + method_poll, + (PRAcceptreadFN)_PR_InvalidInt32, + (PRTransmitfileFN)_PR_InvalidInt32, + (PRGetsocknameFN)_PR_InvalidStatus, + method_getpeername, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + method_getsocketoption, + method_setsocketoption, + (PRSendfileFN)_PR_InvalidInt32, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn +}; + +static void backend_send_if_handler_done (struct BSSLConnection_backend *b, int data_len) +{ + ASSERT(b->send_busy) + ASSERT(b->send_len > 0) + ASSERT(b->send_pos < b->send_len) + ASSERT(data_len > 0) + ASSERT(data_len <= b->send_len - b->send_pos) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->send_buf_mutex); + } + + // update buffer + b->send_pos += data_len; + + // send more if needed + if (b->send_pos < b->send_len) { + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } + return; + } + + // set send not busy + b->send_busy = 0; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } + + // notify connection + if (b->con && !b->con->have_error) { + connection_try_io(b->con); + return; + } +} + +static void backend_recv_if_handler_done (struct BSSLConnection_backend *b, int data_len) +{ + ASSERT(b->recv_busy) + ASSERT(data_len > 0) + ASSERT(data_len <= BSSLCONNECTION_BUF_SIZE) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->recv_buf_mutex); + } + + // init buffer + b->recv_busy = 0; + b->recv_pos = 0; + b->recv_len = data_len; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->recv_buf_mutex); + } + + // notify connection + if (b->con && !b->con->have_error) { + connection_try_io(b->con); + return; + } +} + +static void backend_threadwork_start (struct BSSLConnection_backend *b, int op) +{ + ASSERT(b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + ASSERT(op == THREADWORK_STATE_HANDSHAKE || op == THREADWORK_STATE_READ || op == THREADWORK_STATE_WRITE) + + b->threadwork_state = op; + b->threadwork_want_recv = 0; + b->threadwork_want_send = 0; + BThreadWork_Init(&b->threadwork, b->twd, connection_threadwork_handler_done, b->con, connection_threadwork_func_work, b->con); +} + +static int backend_threadwork_do_io (struct BSSLConnection_backend *b) +{ + ASSERT(b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + + int io_ready = (b->threadwork_want_recv && !b->recv_busy && b->recv_pos < b->recv_len) || + (b->threadwork_want_send && b->send_pos == b->send_len); + + if (b->threadwork_want_recv && b->recv_pos == b->recv_len && !b->recv_busy) { + b->recv_busy = 1; + StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); + } + + if (b->send_pos < b->send_len && !b->send_busy) { + b->send_busy = 1; + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + } + + return io_ready; +} + +static void connection_report_error (BSSLConnection *o) +{ + ASSERT(!o->have_error) + + // set error + o->have_error = 1; + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BSSLCONNECTION_EVENT_ERROR)); +} + +static void connection_init_job_handler (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(!o->up) + + connection_try_handshake(o); +} + +static void connection_init_up (BSSLConnection *o) +{ + // unset init job + // (just in the impossible case that handshake completed before the init job executed) + BPending_Unset(&o->init_job); + + // init send interface + StreamPassInterface_Init(&o->send_if, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, o->pg); + + // init recv interface + StreamRecvInterface_Init(&o->recv_if, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, o->pg); + + // init recv job + BPending_Init(&o->recv_job, o->pg, (BPending_handler)connection_recv_job_handler, o); + + // set no send data + o->send_len = -1; + + // set no recv data + o->recv_avail = -1; + + // set up + o->up = 1; +} + +static void connection_try_io (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + + if (!o->up) { + connection_try_handshake(o); + return; + } + + if (o->send_len > 0) { + if (o->recv_avail > 0) { + BPending_Set(&o->recv_job); + } + + connection_try_send(o); + return; + } + + if (o->recv_avail > 0) { + connection_try_recv(o); + return; + } +} + +static void connection_threadwork_func_work (void *user) +{ + BSSLConnection *o = user; + struct BSSLConnection_backend *b = o->backend; + ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) + + switch (b->threadwork_state) { + case THREADWORK_STATE_HANDSHAKE: + b->threadwork_result_sec = SSL_ForceHandshake(o->prfd); + break; + case THREADWORK_STATE_WRITE: + b->threadwork_result_pr = PR_Write(o->prfd, o->send_data, o->send_len); + break; + case THREADWORK_STATE_READ: + b->threadwork_result_pr = PR_Read(o->prfd, o->recv_data, o->recv_avail); + break; + default: + ASSERT(0); + } + + b->threadwork_error = PR_GetError(); +} + +static void connection_threadwork_handler_done (void *user) +{ + BSSLConnection *o = user; + struct BSSLConnection_backend *b = o->backend; + ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) + + // remember what operation the threadwork was performing + int op = b->threadwork_state; + + // free threadwork + BThreadWork_Free(&b->threadwork); + b->threadwork_state = THREADWORK_STATE_NONE; + + // start any necessary backend I/O operations, and determine if any of the requested + // backend I/O that was not available at the time is now available + int io_ready = backend_threadwork_do_io(b); + + switch (op) { + case THREADWORK_STATE_HANDSHAKE: { + ASSERT(!o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) + + if (b->threadwork_result_sec == SECFailure) { + if (b->threadwork_error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); + } + return; + } + BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", b->threadwork_error); + connection_report_error(o); + return; + } + + // init up + connection_init_up(o); + + // report up + o->handler(o->user, BSSLCONNECTION_EVENT_UP); + return; + } break; + + case THREADWORK_STATE_WRITE: { + ASSERT(o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) + ASSERT(o->send_len > 0) + + PRInt32 result = b->threadwork_result_pr; + PRErrorCode error = b->threadwork_error; + + if (result < 0) { + if (error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } else if (o->recv_avail > 0) { + // don't forget about receiving + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + return; + } + BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + ASSERT(result > 0) + ASSERT(result <= o->send_len) + + // set no send data + o->send_len = -1; + + // don't forget about receiving + if (o->recv_avail > 0) { + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + + // finish send operation + StreamPassInterface_Done(&o->send_if, result); + } break; + + case THREADWORK_STATE_READ: { + ASSERT(o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) + ASSERT(o->recv_avail > 0) + + PRInt32 result = b->threadwork_result_pr; + PRErrorCode error = b->threadwork_error; + + if (result < 0) { + if (error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } else if (o->send_len > 0) { + // don't forget about sending + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + return; + } + BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + if (result == 0) { + BLog(BLOG_ERROR, "PR_Read returned 0"); + connection_report_error(o); + return; + } + + ASSERT(result > 0) + ASSERT(result <= o->recv_avail) + + // set no recv data + o->recv_avail = -1; + + // don't forget about sending + if (o->send_len > 0) { + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + + // finish receive operation + StreamRecvInterface_Done(&o->recv_if, result); + } break; + + default: + ASSERT(0); + } + + return; +} + +static void connection_recv_job_handler (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail > 0) + + connection_try_recv(o); + return; +} + +static void connection_try_handshake (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(!o->up) + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); + } + return; + } + + // try handshake + SECStatus res = SSL_ForceHandshake(o->prfd); + if (res == SECFailure) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + // init up + connection_init_up(o); + + // report up + o->handler(o->user, BSSLCONNECTION_EVENT_UP); + return; +} + +static void connection_try_send (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->send_len > 0) + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + return; + } + + // send + PRInt32 res = PR_Write(o->prfd, o->send_data, o->send_len); + if (res < 0) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + ASSERT(res > 0) + ASSERT(res <= o->send_len) + + // set no send data + o->send_len = -1; + + // done + StreamPassInterface_Done(&o->send_if, res); +} + +static void connection_try_recv (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail > 0) + + // unset recv job + BPending_Unset(&o->recv_job); + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + return; + } + + // recv + PRInt32 res = PR_Read(o->prfd, o->recv_data, o->recv_avail); + if (res < 0) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + if (res == 0) { + BLog(BLOG_ERROR, "PR_Read returned 0"); + connection_report_error(o); + return; + } + + ASSERT(res > 0) + ASSERT(res <= o->recv_avail) + + // set no recv data + o->recv_avail = -1; + + // done + StreamRecvInterface_Done(&o->recv_if, res); +} + +static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->send_len == -1) + ASSERT(data_len > 0) + +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) + o->user_io_started = 1; +#endif + + // limit amount for PR_Write + if (data_len > INT32_MAX) { + data_len = INT32_MAX; + } + + // set send data + o->send_data = data; + o->send_len = data_len; + + // start sending + connection_try_send(o); +} + +static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail == -1) + ASSERT(data_len > 0) + +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) + o->user_io_started = 1; +#endif + + // limit amount for PR_Read + if (data_len > INT32_MAX) { + data_len = INT32_MAX; + } + + // set recv data + o->recv_data = data; + o->recv_avail = data_len; + + // start receiving + connection_try_recv(o); +} + +int BSSLConnection_GlobalInit (void) +{ + ASSERT(!bprconnection_initialized) + + if ((bprconnection_identity = PR_GetUniqueIdentity("BSSLConnection")) == PR_INVALID_IO_LAYER) { + BLog(BLOG_ERROR, "PR_GetUniqueIdentity failed"); + return 0; + } + + bprconnection_initialized = 1; + + return 1; +} + +int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags) +{ + ASSERT(bprconnection_initialized) + ASSERT(!(flags & ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO))) + ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || twd) + ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_IO) || twd) + + // don't do stuff in threads if threads aren't available + if (((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) && + !BThreadWorkDispatcher_UsingThreads(twd) + ) { + BLog(BLOG_WARNING, "SSL operations in threads requested but threads are not available"); + flags &= ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO); + } + + // allocate backend + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)malloc(sizeof(*b)); + if (!b) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init mutexes + if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (!BMutex_Init(&b->send_buf_mutex)) { + BLog(BLOG_ERROR, "BMutex_Init failed"); + goto fail1; + } + + if (!BMutex_Init(&b->recv_buf_mutex)) { + BLog(BLOG_ERROR, "BMutex_Init failed"); + goto fail2; + } + } + + // init arguments + b->send_if = send_if; + b->recv_if = recv_if; + b->twd = twd; + b->flags = flags; + + // init interfaces + StreamPassInterface_Sender_Init(b->send_if, (StreamPassInterface_handler_done)backend_send_if_handler_done, b); + StreamRecvInterface_Receiver_Init(b->recv_if, (StreamRecvInterface_handler_done)backend_recv_if_handler_done, b); + + // set no connection + b->con = NULL; + + // init send buffer + b->send_busy = 0; + b->send_len = 0; + b->send_pos = 0; + + // init recv buffer + b->recv_busy = 0; + b->recv_pos = 0; + b->recv_len = 0; + + // set threadwork state + b->threadwork_state = THREADWORK_STATE_NONE; + + // init prfd + memset(prfd, 0, sizeof(*prfd)); + prfd->methods = &methods; + prfd->secret = (PRFilePrivate *)b; + prfd->identity = bprconnection_identity; + + return 1; + + if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { +fail2: + BMutex_Free(&b->send_buf_mutex); + } +fail1: + free(b); +fail0: + return 0; +} + +void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user, + BSSLConnection_handler handler) +{ + ASSERT(force_handshake == 0 || force_handshake == 1) + ASSERT(handler) + ASSERT(bprconnection_initialized) + ASSERT(get_bottom(prfd)->identity == bprconnection_identity) + ASSERT(!((struct BSSLConnection_backend *)(get_bottom(prfd)->secret))->con) + + // init arguments + o->prfd = prfd; + o->pg = pg; + o->user = user; + o->handler = handler; + + // set backend + o->backend = (struct BSSLConnection_backend *)(get_bottom(prfd)->secret); + ASSERT(!o->backend->con) + ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) + + // set have no error + o->have_error = 0; + + // init init job + BPending_Init(&o->init_job, o->pg, (BPending_handler)connection_init_job_handler, o); + + if (force_handshake) { + // set not up + o->up = 0; + + // set init job + BPending_Set(&o->init_job); + } else { + // init up + connection_init_up(o); + } + + // set backend connection + o->backend->con = o; + +#ifndef NDEBUG + o->user_io_started = 0; + o->releasebuffers_called = 0; +#endif + + DebugError_Init(&o->d_err, o->pg); + DebugObject_Init(&o->d_obj); +} + +void BSSLConnection_Free (BSSLConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +#ifndef NDEBUG + ASSERT(o->releasebuffers_called || !o->user_io_started) +#endif + ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) + + if (o->up) { + // free recv job + BPending_Free(&o->recv_job); + + // free recv interface + StreamRecvInterface_Free(&o->recv_if); + + // free send interface + StreamPassInterface_Free(&o->send_if); + } + + // free init job + BPending_Free(&o->init_job); + + // unset backend connection + o->backend->con = NULL; +} + +void BSSLConnection_ReleaseBuffers (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) +#endif + + // wait for threadwork to finish + if (o->backend->threadwork_state != THREADWORK_STATE_NONE) { + BThreadWork_Free(&o->backend->threadwork); + o->backend->threadwork_state = THREADWORK_STATE_NONE; + } + +#ifndef NDEBUG + o->releasebuffers_called = 1; +#endif +} + +StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return &o->send_if; +} + +StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return &o->recv_if; +} diff --git a/app/src/main/jni/badvpn/nspr_support/BSSLConnection.h b/app/src/main/jni/badvpn/nspr_support/BSSLConnection.h new file mode 100644 index 0000000..1152cac --- /dev/null +++ b/app/src/main/jni/badvpn/nspr_support/BSSLConnection.h @@ -0,0 +1,116 @@ +/** + * @file BSSLConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BSSLCONNECTION_H +#define BADVPN_BSSLCONNECTION_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BSSLCONNECTION_EVENT_UP 1 +#define BSSLCONNECTION_EVENT_ERROR 2 + +#define BSSLCONNECTION_BUF_SIZE 4096 + +#define BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE (1 << 0) +#define BSSLCONNECTION_FLAG_THREADWORK_IO (1 << 1) + +typedef void (*BSSLConnection_handler) (void *user, int event); + +struct BSSLConnection_backend; + +typedef struct { + PRFileDesc *prfd; + BPendingGroup *pg; + void *user; + BSSLConnection_handler handler; + struct BSSLConnection_backend *backend; + int have_error; + int up; + BPending init_job; + StreamPassInterface send_if; + StreamRecvInterface recv_if; + BPending recv_job; + const uint8_t *send_data; + int send_len; + uint8_t *recv_data; + int recv_avail; +#ifndef NDEBUG + int user_io_started; + int releasebuffers_called; +#endif + DebugError d_err; + DebugObject d_obj; +} BSSLConnection; + +struct BSSLConnection_backend { + StreamPassInterface *send_if; + StreamRecvInterface *recv_if; + BThreadWorkDispatcher *twd; + int flags; + BSSLConnection *con; + uint8_t send_buf[BSSLCONNECTION_BUF_SIZE]; + int send_busy; + int send_pos; + int send_len; + uint8_t recv_buf[BSSLCONNECTION_BUF_SIZE]; + int recv_busy; + int recv_pos; + int recv_len; + int threadwork_state; + int threadwork_want_recv; + int threadwork_want_send; + BThreadWork threadwork; + SECStatus threadwork_result_sec; + PRInt32 threadwork_result_pr; + PRErrorCode threadwork_error; + BMutex send_buf_mutex; + BMutex recv_buf_mutex; +}; + +int BSSLConnection_GlobalInit (void) WARN_UNUSED; +int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags) WARN_UNUSED; + +void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user, + BSSLConnection_handler handler); +void BSSLConnection_Free (BSSLConnection *o); +void BSSLConnection_ReleaseBuffers (BSSLConnection *o); +StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o); +StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o); + +#endif diff --git a/app/src/main/jni/badvpn/nspr_support/CMakeLists.txt b/app/src/main/jni/badvpn/nspr_support/CMakeLists.txt new file mode 100644 index 0000000..b70eaf6 --- /dev/null +++ b/app/src/main/jni/badvpn/nspr_support/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(nspr_support + DummyPRFileDesc.c + BSSLConnection.c +) +target_link_libraries(nspr_support system flow threadwork ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) diff --git a/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.c b/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.c new file mode 100644 index 0000000..543a3d5 --- /dev/null +++ b/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.c @@ -0,0 +1,176 @@ +/** + * @file DummyPRFileDesc.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#ifndef NDEBUG +int dummyprfiledesc_initialized = 0; +#endif +PRDescIdentity dummyprfiledesc_identity; + +static PRStatus method_close (PRFileDesc *fd) +{ + return PR_SUCCESS; +} + +static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRIntn _PR_InvalidIntn (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt16 _PR_InvalidInt16 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt32 _PR_InvalidInt32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt64 _PR_InvalidInt64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset32 _PR_InvalidOffset32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset64 _PR_InvalidOffset64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRStatus _PR_InvalidStatus (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc *_PR_InvalidDesc (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +static PRIOMethods methods = { + (PRDescType)0, + method_close, + (PRReadFN)_PR_InvalidInt32, + (PRWriteFN)_PR_InvalidInt32, + (PRAvailableFN)_PR_InvalidInt32, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidOffset32, + (PRSeek64FN)_PR_InvalidOffset64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt32, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt32, + (PRSendFN)_PR_InvalidInt32, + (PRRecvfromFN)_PR_InvalidInt32, + (PRSendtoFN)_PR_InvalidInt32, + (PRPollFN)_PR_InvalidInt16, + (PRAcceptreadFN)_PR_InvalidInt32, + (PRTransmitfileFN)_PR_InvalidInt32, + (PRGetsocknameFN)_PR_InvalidStatus, + method_getpeername, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt32, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn +}; + +int DummyPRFileDesc_GlobalInit (void) +{ + ASSERT(!dummyprfiledesc_initialized) + + if ((dummyprfiledesc_identity = PR_GetUniqueIdentity("DummyPRFileDesc")) == PR_INVALID_IO_LAYER) { + return 0; + } + + #ifndef NDEBUG + dummyprfiledesc_initialized = 1; + #endif + + return 1; +} + +void DummyPRFileDesc_Create (PRFileDesc *prfd) +{ + ASSERT(dummyprfiledesc_initialized) + + memset(prfd, 0, sizeof(*prfd)); + prfd->methods = &methods; + prfd->secret = NULL; + prfd->identity = dummyprfiledesc_identity; +} diff --git a/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.h b/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.h new file mode 100644 index 0000000..413f105 --- /dev/null +++ b/app/src/main/jni/badvpn/nspr_support/DummyPRFileDesc.h @@ -0,0 +1,61 @@ +/** + * @file DummyPRFileDesc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dummy NSPR file descriptor (PRFileDesc). + * Used for creating a model SSL file descriptor to cache various stuff + * to improve performance. + */ + +#ifndef BADVPN_NSPRSUPPORT_DUMMYPRFILEDESC_H +#define BADVPN_NSPRSUPPORT_DUMMYPRFILEDESC_H + +#include + +#include + +extern PRDescIdentity dummyprfiledesc_identity; + +/** + * Globally initialize the dummy NSPR file descriptor backend. + * Must not have been called successfully. + * + * @return 1 on success, 0 on failure + */ +int DummyPRFileDesc_GlobalInit (void) WARN_UNUSED; + +/** + * Creates a dummy NSPR file descriptor. + * {@link DummyPRFileDesc_GlobalInit} must have been done. + * + * @param prfd uninitialized PRFileDesc structure + */ +void DummyPRFileDesc_Create (PRFileDesc *prfd); + +#endif diff --git a/app/src/main/jni/badvpn/predicate/BPredicate.c b/app/src/main/jni/badvpn/predicate/BPredicate.c new file mode 100644 index 0000000..5d5dadb --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate.c @@ -0,0 +1,284 @@ +/** + * @file BPredicate.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int eval_predicate_node (BPredicate *p, struct predicate_node *root); + +void yyerror (YYLTYPE *yylloc, yyscan_t scanner, struct predicate_node **result, char *str) +{ +} + +static int string_comparator (void *user, char *s1, char *s2) +{ + int cmp = strcmp(s1, s2); + return B_COMPARE(cmp, 0); +} + +static int eval_function (BPredicate *p, struct predicate_node *root) +{ + ASSERT(root->type == NODE_FUNCTION) + + // lookup function by name + ASSERT(root->function.name) + BAVLNode *tree_node; + if (!(tree_node = BAVL_LookupExact(&p->functions_tree, root->function.name))) { + BLog(BLOG_WARNING, "unknown function"); + return 0; + } + BPredicateFunction *func = UPPER_OBJECT(tree_node, BPredicateFunction, tree_node); + + // evaluate arguments + struct arguments_node *arg = root->function.args; + void *args[PREDICATE_MAX_ARGS]; + for (int i = 0; i < func->num_args; i++) { + if (!arg) { + BLog(BLOG_WARNING, "not enough arguments"); + return 0; + } + switch (func->args[i]) { + case PREDICATE_TYPE_BOOL: + if (arg->arg.type != ARGUMENT_PREDICATE) { + BLog(BLOG_WARNING, "expecting predicate argument"); + return 0; + } + if (!eval_predicate_node(p, arg->arg.predicate)) { + return 0; + } + args[i] = &arg->arg.predicate->eval_value; + break; + case PREDICATE_TYPE_STRING: + if (arg->arg.type != ARGUMENT_STRING) { + BLog(BLOG_WARNING, "expecting string argument"); + return 0; + } + args[i] = arg->arg.string; + break; + default: + ASSERT(0); + } + arg = arg->next; + } + + if (arg) { + BLog(BLOG_WARNING, "too many arguments"); + return 0; + } + + // call callback + #ifndef NDEBUG + p->in_function = 1; + #endif + int res = func->callback(func->user, args); + #ifndef NDEBUG + p->in_function = 0; + #endif + if (res != 0 && res != 1) { + BLog(BLOG_WARNING, "callback returned non-boolean"); + return 0; + } + + root->eval_value = res; + return 1; +} + +int eval_predicate_node (BPredicate *p, struct predicate_node *root) +{ + ASSERT(root) + + switch (root->type) { + case NODE_CONSTANT: + root->eval_value = root->constant.val; + return 1; + case NODE_NEG: + if (!eval_predicate_node(p, root->neg.op)) { + return 0; + } + root->eval_value = !root->neg.op->eval_value; + return 1; + case NODE_CONJUNCT: + if (!eval_predicate_node(p, root->conjunct.op1)) { + return 0; + } + if (!root->conjunct.op1->eval_value) { + root->eval_value = 0; + return 1; + } + if (!eval_predicate_node(p, root->conjunct.op2)) { + return 0; + } + if (!root->conjunct.op2->eval_value) { + root->eval_value = 0; + return 1; + } + root->eval_value = 1; + return 1; + case NODE_DISJUNCT: + if (!eval_predicate_node(p, root->disjunct.op1)) { + return 0; + } + if (root->disjunct.op1->eval_value) { + root->eval_value = 1; + return 1; + } + if (!eval_predicate_node(p, root->disjunct.op2)) { + return 0; + } + if (root->disjunct.op2->eval_value) { + root->eval_value = 1; + return 1; + } + root->eval_value = 0; + return 1; + case NODE_FUNCTION: + return eval_function(p, root); + default: + ASSERT(0) + return 0; + } +} + +int BPredicate_Init (BPredicate *p, char *str) +{ + // initialize input buffer object + LexMemoryBufferInput input; + LexMemoryBufferInput_Init(&input, str, strlen(str)); + + // initialize lexical analyzer + yyscan_t scanner; + yylex_init_extra(&input, &scanner); + + // parse + struct predicate_node *root = NULL; + int result = yyparse(scanner, &root); + + // free lexical analyzer + yylex_destroy(scanner); + + // check for errors + if (LexMemoryBufferInput_HasError(&input) || result != 0 || !root) { + if (root) { + free_predicate_node(root); + } + return 0; + } + + // init tree + p->root = root; + + // init functions tree + BAVL_Init(&p->functions_tree, OFFSET_DIFF(BPredicateFunction, name, tree_node), (BAVL_comparator)string_comparator, NULL); + + // init debuggind + #ifndef NDEBUG + p->in_function = 0; + #endif + + // init debug object + DebugObject_Init(&p->d_obj); + + return 1; +} + +void BPredicate_Free (BPredicate *p) +{ + ASSERT(BAVL_IsEmpty(&p->functions_tree)) + ASSERT(!p->in_function) + + // free debug object + DebugObject_Free(&p->d_obj); + + // free tree + free_predicate_node((struct predicate_node *)p->root); +} + +int BPredicate_Eval (BPredicate *p) +{ + ASSERT(!p->in_function) + + if (!eval_predicate_node(p, (struct predicate_node *)p->root)) { + return -1; + } + + return ((struct predicate_node *)p->root)->eval_value; +} + +void BPredicateFunction_Init (BPredicateFunction *o, BPredicate *p, char *name, int *args, int num_args, BPredicate_callback callback, void *user) +{ + ASSERT(strlen(name) <= PREDICATE_MAX_NAME) + ASSERT(!BAVL_LookupExact(&p->functions_tree, name)) + ASSERT(num_args >= 0) + ASSERT(num_args <= PREDICATE_MAX_ARGS) + for (int i = 0; i < num_args; i++) { + ASSERT(args[i] == PREDICATE_TYPE_BOOL || args[i] == PREDICATE_TYPE_STRING) + } + ASSERT(!p->in_function) + + // init arguments + o->p = p; + strcpy(o->name, name); + memcpy(o->args, args, num_args * sizeof(int)); + o->num_args = num_args; + o->callback = callback; + o->user = user; + + // add to tree + ASSERT_EXECUTE(BAVL_Insert(&p->functions_tree, &o->tree_node, NULL)) + + // init debug object + DebugObject_Init(&o->d_obj); +} + +void BPredicateFunction_Free (BPredicateFunction *o) +{ + ASSERT(!o->p->in_function) + + BPredicate *p = o->p; + + // free debug object + DebugObject_Free(&o->d_obj); + + // remove from tree + BAVL_Remove(&p->functions_tree, &o->tree_node); +} diff --git a/app/src/main/jni/badvpn/predicate/BPredicate.h b/app/src/main/jni/badvpn/predicate/BPredicate.h new file mode 100644 index 0000000..04b3aa5 --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate.h @@ -0,0 +1,177 @@ +/** + * @file BPredicate.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that parses and evaluates a logical expression. + * Allows the user to define custom functions than can be + * used in the expression. + * + * Syntax and semantics for logical expressions: + * + * - true + * Logical true constant. Evaluates to 1. + * + * - false + * Logical false constant. Evaluates to 0. + * + * - NOT expression + * Logical negation. If the expression evaluates to error, the + * negation evaluates to error. + * + * - expression OR expression + * Logical disjunction. The second expression is only evaluated + * if the first expression evaluates to false. If a sub-expression + * evaluates to error, the disjunction evaluates to error. + * + * - expression AND expression + * Logical conjunction. The second expression is only evaluated + * if the first expression evaluates to true. If a sub-expression + * evaluates to error, the conjunction evaluates to error. + * + * - function(arg, ..., arg) + * Evaluation of a user-provided function (function is the name of the + * function, [a-zA-Z0-9_]+). + * If the function with the given name does not exist, it evaluates to + * error. + * Arguments are evaluated from left to right. Each argument can either + * be a logical expression or a string (characters enclosed in double + * quotes, without any double quote). + * If an argument is encountered, but all needed arguments have already + * been evaluated, the function evaluates to error. + * If an argument is of wrong type, it is not evaluated and the function + * evaluates to error. + * If an argument evaluates to error, the function evaluates to error. + * If after all arguments have been evaluated, the function needs more + * arguments, it evaluates to error. + * Then the handler function is called. If it returns anything other + * than 1 and 0, the function evaluates to error. Otherwise it evaluates + * to what the handler function returned. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_H +#define BADVPN_PREDICATE_BPREDICATE_H + +#include +#include +#include + +#define PREDICATE_TYPE_BOOL 1 +#define PREDICATE_TYPE_STRING 2 + +#define PREDICATE_MAX_NAME 16 +#define PREDICATE_MAX_ARGS 16 + +/** + * Handler function called when evaluating a custom function in the predicate. + * + * @param user value passed to {@link BPredicateFunction_Init} + * @param args arguments to the function. Points to an array of pointers (as many as the + * function has arguments), where each pointer points to either to an int or + * a zero-terminated string (depending on the type of the argument). + * @return 1 for true, 0 for false, -1 for error + */ +typedef int (*BPredicate_callback) (void *user, void **args); + +/** + * Object that parses and evaluates a logical expression. + * Allows the user to define custom functions than can be + * used in the expression. + */ +typedef struct { + DebugObject d_obj; + void *root; + BAVL functions_tree; + #ifndef NDEBUG + int in_function; + #endif +} BPredicate; + +/** + * Object that represents a custom function in {@link BPredicate}. + */ +typedef struct { + DebugObject d_obj; + BPredicate *p; + char name[PREDICATE_MAX_NAME + 1]; + int args[PREDICATE_MAX_ARGS]; + int num_args; + BPredicate_callback callback; + void *user; + BAVLNode tree_node; +} BPredicateFunction; + +/** + * Initializes the object. + * + * @param p the object + * @param str logical expression + * @return 1 on success, 0 on failure + */ +int BPredicate_Init (BPredicate *p, char *str) WARN_UNUSED; + +/** + * Frees the object. + * Must have no custom functions. + * Must not be called from function handlers. + * + * @param p the object + */ +void BPredicate_Free (BPredicate *p); + +/** + * Evaluates the logical expression. + * Must not be called from function handlers. + * + * @param p the object + * @return 1 for true, 0 for false, -1 for error + */ +int BPredicate_Eval (BPredicate *p); + +/** + * Registers a custom function for {@link BPredicate}. + * Must not be called from function handlers. + * + * @param o the object + * @param p predicate to register the function for + * @param args array of argument types. Each type is either PREDICATE_TYPE_BOOL or PREDICATE_TYPE_STRING. + * @param num_args number of arguments for the function. Must be >=0 and <=PREDICATE_MAX_ARGS. + * @param callback handler to call to evaluate the function + * @param user value to pass to handler + */ +void BPredicateFunction_Init (BPredicateFunction *o, BPredicate *p, char *name, int *args, int num_args, BPredicate_callback callback, void *user); + +/** + * Removes a custom function for {@link BPredicate}. + * Must not be called from function handlers. + * + * @param o the object + */ +void BPredicateFunction_Free (BPredicateFunction *o); + +#endif diff --git a/app/src/main/jni/badvpn/predicate/BPredicate.l b/app/src/main/jni/badvpn/predicate/BPredicate.l new file mode 100644 index 0000000..71bfd2f --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate.l @@ -0,0 +1,83 @@ +/** + * @file BPredicate.l + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} lexer file. + */ + +%{ + +#include +#include + +#include +#include + +#include + +#define YY_INPUT(buffer, res, max_size) \ + int bytes_read = LexMemoryBufferInput_Read((LexMemoryBufferInput *)yyget_extra(yyscanner), buffer, max_size); \ + res = (bytes_read == 0 ? YY_NULL : bytes_read); + +%} + +%option reentrant stack noyywrap bison-bridge bison-locations never-interactive nounistd + +%% +\( return SPAR; +\) return EPAR; +, return COMMA; +AND return AND; +OR return OR; +NOT return NOT; +true return CONSTANT_TRUE; +false return CONSTANT_FALSE; +[a-zA-Z0-9_]+ { + int l = strlen(yytext); + char *p = (char *)malloc(l + 1); + if (p) { + memcpy(p, yytext, l); + p[l] = '\0'; + } + yylval->text = p; + return NAME; + } +\"[^\"]*\" { + int l = strlen(yytext); + char *p = (char *)malloc(l - 1); + if (p) { + memcpy(p, yytext + 1, l - 2); + p[l - 2] = '\0'; + } + yylval->text = p; + return STRING; + } +[ \t\n]+ ; +. LexMemoryBufferInput_SetError((LexMemoryBufferInput *)yyget_extra(yyscanner)); return 0; // remember failure and report EOF +%% diff --git a/app/src/main/jni/badvpn/predicate/BPredicate.y b/app/src/main/jni/badvpn/predicate/BPredicate.y new file mode 100644 index 0000000..f48df45 --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate.y @@ -0,0 +1,345 @@ +/** + * @file BPredicate.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} grammar file. + */ + +%{ + +#include + +#include +#include + +#define YYLEX_PARAM scanner + +static struct predicate_node * make_constant (int val) +{ + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + return NULL; + } + + n->type = NODE_CONSTANT; + n->constant.val = val; + + return n; +} + +static struct predicate_node * make_negation (struct predicate_node *op) +{ + if (!op) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_NEG; + n->neg.op = op; + + return n; + +fail: + if (op) { + free_predicate_node(op); + } + return NULL; +} + +static struct predicate_node * make_conjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_CONJUNCT; + n->conjunct.op1 = op1; + n->conjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_disjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_DISJUNCT; + n->disjunct.op1 = op1; + n->disjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_function (char *name, struct arguments_node *args, int need_args) +{ + if (!name || (!args && need_args)) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_FUNCTION; + n->function.name = name; + n->function.args = args; + + return n; + +fail: + if (name) { + free(name); + } + if (args) { + free_arguments_node(args); + } + return NULL; +} + +static struct arguments_node * make_arguments (struct arguments_arg arg, struct arguments_node *next, int need_next) +{ + if (arg.type == ARGUMENT_INVALID || (!next && need_next)) { + goto fail; + } + + struct arguments_node *n = (struct arguments_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->arg = arg; + n->next = next; + + return n; + +fail: + free_argument(arg); + if (next) { + free_arguments_node(next); + } + return NULL; +} + +static struct arguments_arg make_argument_predicate (struct predicate_node *pr) +{ + struct arguments_arg ret; + + if (!pr) { + goto fail; + } + + ret.type = ARGUMENT_PREDICATE; + ret.predicate = pr; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +static struct arguments_arg make_argument_string (char *string) +{ + struct arguments_arg ret; + + if (!string) { + goto fail; + } + + ret.type = ARGUMENT_STRING; + ret.string = string; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +%} + +%pure-parser +%locations +%parse-param {void *scanner} +%parse-param {struct predicate_node **result} + +%union { + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; +}; + +// token types +%token STRING NAME +%token PEER1_NAME PEER2_NAME AND OR NOT SPAR EPAR CONSTANT_TRUE CONSTANT_FALSE COMMA + +// string token destructor +%destructor { + free($$); +} STRING NAME + +// return values +%type predicate constant parentheses neg conjunct disjunct function +%type arguments +%type argument + +// predicate node destructor +%destructor { + if ($$) { + free_predicate_node($$); + } +} predicate constant parentheses neg conjunct disjunct function + +// argument node destructor +%destructor { + if ($$) { + free_arguments_node($$); + } +} arguments + +// argument argument destructor +%destructor { + free_argument($$); +} argument + +%left OR +%left AND +%nonassoc NOT +%right COMMA + +%% + +input: + predicate { + *result = $1; + } + ; + +predicate: constant | parentheses | neg | conjunct | disjunct | function; + +constant: + CONSTANT_TRUE { + $$ = make_constant(1); + } + | + CONSTANT_FALSE { + $$ = make_constant(0); + } + ; + +parentheses: + SPAR predicate EPAR { + $$ = $2; + } + ; + +neg: + NOT predicate { + $$ = make_negation($2); + } + ; + +conjunct: + predicate AND predicate { + $$ = make_conjunction($1, $3); + } + ; + +disjunct: + predicate OR predicate { + $$ = make_disjunction($1, $3); + } + ; + +function: + NAME SPAR EPAR { + $$ = make_function($1, NULL, 0); + } + | + NAME SPAR arguments EPAR { + $$ = make_function($1, $3, 1); + } + ; + +arguments: + argument { + $$ = make_arguments($1, NULL, 0); + } + | + argument COMMA arguments { + $$ = make_arguments($1, $3, 1); + } + ; + +argument: + predicate { + $$ = make_argument_predicate($1); + } + | + STRING { + $$ = make_argument_string($1); + } + ; diff --git a/app/src/main/jni/badvpn/predicate/BPredicate_internal.h b/app/src/main/jni/badvpn/predicate/BPredicate_internal.h new file mode 100644 index 0000000..12db554 --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate_internal.h @@ -0,0 +1,154 @@ +/** + * @file BPredicate_internal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} expression tree definitions and functions. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_INTERNAL_H +#define BADVPN_PREDICATE_BPREDICATE_INTERNAL_H + +#include + +#define NODE_CONSTANT 0 +#define NODE_NEG 2 +#define NODE_CONJUNCT 3 +#define NODE_DISJUNCT 4 +#define NODE_FUNCTION 5 + +struct arguments_node; + +struct predicate_node { + int type; + union { + struct { + int val; + } constant; + struct { + struct predicate_node *op; + } neg; + struct { + struct predicate_node *op1; + struct predicate_node *op2; + } conjunct; + struct { + struct predicate_node *op1; + struct predicate_node *op2; + } disjunct; + struct { + char *name; + struct arguments_node *args; + } function; + }; + int eval_value; +}; + +#define ARGUMENT_INVALID 0 +#define ARGUMENT_PREDICATE 1 +#define ARGUMENT_STRING 2 + +struct arguments_arg { + int type; + union { + struct predicate_node *predicate; + char *string; + }; +}; + +struct arguments_node { + struct arguments_arg arg; + struct arguments_node *next; +}; + +static void free_predicate_node (struct predicate_node *root); +static void free_argument (struct arguments_arg arg); +static void free_arguments_node (struct arguments_node *n); + +void free_predicate_node (struct predicate_node *root) +{ + ASSERT(root) + + switch (root->type) { + case NODE_CONSTANT: + break; + case NODE_NEG: + free_predicate_node(root->neg.op); + break; + case NODE_CONJUNCT: + free_predicate_node(root->conjunct.op1); + free_predicate_node(root->conjunct.op2); + break; + case NODE_DISJUNCT: + free_predicate_node(root->disjunct.op1); + free_predicate_node(root->disjunct.op2); + break; + case NODE_FUNCTION: + free(root->function.name); + if (root->function.args) { + free_arguments_node(root->function.args); + } + break; + default: + ASSERT(0) + break; + } + + free(root); +} + +void free_argument (struct arguments_arg arg) +{ + switch (arg.type) { + case ARGUMENT_INVALID: + break; + case ARGUMENT_PREDICATE: + free_predicate_node(arg.predicate); + break; + case ARGUMENT_STRING: + free(arg.string); + break; + default: + ASSERT(0); + } +} + +void free_arguments_node (struct arguments_node *n) +{ + ASSERT(n) + + free_argument(n->arg); + + if (n->next) { + free_arguments_node(n->next); + } + + free(n); +} + +#endif diff --git a/app/src/main/jni/badvpn/predicate/BPredicate_parser.h b/app/src/main/jni/badvpn/predicate/BPredicate_parser.h new file mode 100644 index 0000000..e7f4a7b --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/BPredicate_parser.h @@ -0,0 +1,47 @@ +/** + * @file BPredicate_parser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} parser definitions. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_PARSER_H +#define BADVPN_PREDICATE_BPREDICATE_PARSER_H + +#define YY_NO_UNISTD_H + +#include + +#include +#include + +// implemented in BPredicate.c +void yyerror (YYLTYPE *yylloc, yyscan_t scanner, struct predicate_node **result, char *str); + +#endif diff --git a/app/src/main/jni/badvpn/predicate/CMakeLists.txt b/app/src/main/jni/badvpn/predicate/CMakeLists.txt new file mode 100644 index 0000000..c094c55 --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(predicate + BPredicate.c + ${PROJECT_SOURCE_DIR}/generated/flex_BPredicate.c + ${PROJECT_SOURCE_DIR}/generated/bison_BPredicate.c +) +target_link_libraries(predicate system) diff --git a/app/src/main/jni/badvpn/predicate/LexMemoryBufferInput.h b/app/src/main/jni/badvpn/predicate/LexMemoryBufferInput.h new file mode 100644 index 0000000..e8f1730 --- /dev/null +++ b/app/src/main/jni/badvpn/predicate/LexMemoryBufferInput.h @@ -0,0 +1,86 @@ +/** + * @file LexMemoryBufferInput.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that can be used by a lexer to read input from a memory buffer. + */ + +#ifndef BADVPN_PREDICATE_LEXMEMORYBUFFERINPUT_H +#define BADVPN_PREDICATE_LEXMEMORYBUFFERINPUT_H + +#include + +#include + +typedef struct { + char *buf; + int len; + int pos; + int error; +} LexMemoryBufferInput; + +static void LexMemoryBufferInput_Init (LexMemoryBufferInput *input, char *buf, int len) +{ + input->buf = buf; + input->len = len; + input->pos = 0; + input->error = 0; +} + +static int LexMemoryBufferInput_Read (LexMemoryBufferInput *input, char *dest, int len) +{ + ASSERT(dest) + ASSERT(len > 0) + + if (input->pos >= input->len) { + return 0; + } + + int to_read = input->len - input->pos; + if (to_read > len) { + to_read = len; + } + + memcpy(dest, input->buf + input->pos, to_read); + input->pos += to_read; + + return to_read; +} + +static void LexMemoryBufferInput_SetError (LexMemoryBufferInput *input) +{ + input->error = 1; +} + +static int LexMemoryBufferInput_HasError (LexMemoryBufferInput *input) +{ + return input->error; +} + +#endif diff --git a/app/src/main/jni/badvpn/protocol/addr.bproto b/app/src/main/jni/badvpn/protocol/addr.bproto new file mode 100644 index 0000000..f020350 --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/addr.bproto @@ -0,0 +1,11 @@ +// message for an AddrProto address +message addr { + // address type. from addr.h + required uint8 type = 1; + // for IPv4 and IPv6 addresses, the port (network byte order) + optional data("2") ip_port = 2; + // for IPv4 addresses, the IP address + optional data("4") ipv4_addr = 3; + // for IPv6 addresses, the IP address + optional data("16") ipv6_addr = 4; +}; diff --git a/app/src/main/jni/badvpn/protocol/addr.h b/app/src/main/jni/badvpn/protocol/addr.h new file mode 100644 index 0000000..9d20265 --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/addr.h @@ -0,0 +1,207 @@ +/** + * @file addr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * AddrProto, a protocol for encoding network addresses. + * + * AddrProto is built with BProto, the protocol and code generator for building + * custom message protocols. The BProto specification file is addr.bproto. + */ + +#ifndef BADVPN_PROTOCOL_ADDR_H +#define BADVPN_PROTOCOL_ADDR_H + +#include +#include + +#include +#include +#include + +#include + +#include + +#define ADDR_TYPE_IPV4 1 +#define ADDR_TYPE_IPV6 2 + +#define ADDR_SIZE_IPV4 (addr_SIZEtype + addr_SIZEip_port + addr_SIZEipv4_addr) +#define ADDR_SIZE_IPV6 (addr_SIZEtype + addr_SIZEip_port + addr_SIZEipv6_addr) + +/** + * Determines if the given address is supported by AddrProto. + * Depends only on the type of the address. + * + * @param addr address to check. Must be recognized according to {@link BAddr_IsRecognized}. + * @return 1 if supported, 0 if not + */ +static int addr_supported (BAddr addr); + +/** + * Determines the size of the given address when encoded by AddrProto. + * Depends only on the type of the address. + * + * @param addr address to check. Must be supported according to {@link addr_supported}. + * @return encoded size + */ +static int addr_size (BAddr addr); + +/** + * Encodes an address according to AddrProto. + * + * @param out output buffer. Must have at least addr_size(addr) space. + * @param addr address to encode. Must be supported according to {@link addr_supported}. + */ +static void addr_write (uint8_t *out, BAddr addr); + +/** + * Decodes an address according to AddrProto. + * + * @param data input buffer containing the address to decode + * @param data_len size of input. Must be >=0. + * @param out_addr the decoded address will be stored here on success + * @return 1 on success, 0 on failure + */ +static int addr_read (uint8_t *data, int data_len, BAddr *out_addr) WARN_UNUSED; + +int addr_supported (BAddr addr) +{ + BAddr_Assert(&addr); + + switch (addr.type) { + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + return 1; + default: + return 0; + } +} + +int addr_size (BAddr addr) +{ + ASSERT(addr_supported(addr)) + + switch (addr.type) { + case BADDR_TYPE_IPV4: + return ADDR_SIZE_IPV4; + case BADDR_TYPE_IPV6: + return ADDR_SIZE_IPV6; + default: + ASSERT(0) + return 0; + } +} + +void addr_write (uint8_t *out, BAddr addr) +{ + ASSERT(addr_supported(addr)) + + addrWriter writer; + addrWriter_Init(&writer, out); + + switch (addr.type) { + case BADDR_TYPE_IPV4: { + addrWriter_Addtype(&writer, ADDR_TYPE_IPV4); + uint8_t *out_port = addrWriter_Addip_port(&writer); + memcpy(out_port, &addr.ipv4.port, sizeof(addr.ipv4.port)); + uint8_t *out_addr = addrWriter_Addipv4_addr(&writer); + memcpy(out_addr, &addr.ipv4.ip, sizeof(addr.ipv4.ip)); + } break; + case BADDR_TYPE_IPV6: { + addrWriter_Addtype(&writer, ADDR_TYPE_IPV6); + uint8_t *out_port = addrWriter_Addip_port(&writer); + memcpy(out_port, &addr.ipv6.port, sizeof(addr.ipv6.port)); + uint8_t *out_addr = addrWriter_Addipv6_addr(&writer); + memcpy(out_addr, addr.ipv6.ip, sizeof(addr.ipv6.ip)); + } break; + default: + ASSERT(0); + } + + int len = addrWriter_Finish(&writer); + B_USE(len) + + ASSERT(len == addr_size(addr)) +} + +int addr_read (uint8_t *data, int data_len, BAddr *out_addr) +{ + ASSERT(data_len >= 0) + + addrParser parser; + if (!addrParser_Init(&parser, data, data_len)) { + BLog(BLOG_ERROR, "failed to parse addr"); + return 0; + } + + uint8_t type = 0; // to remove warning + addrParser_Gettype(&parser, &type); + + switch (type) { + case ADDR_TYPE_IPV4: { + uint8_t *port_data; + if (!addrParser_Getip_port(&parser, &port_data)) { + BLog(BLOG_ERROR, "port missing for IPv4 address"); + return 0; + } + uint8_t *addr_data; + if (!addrParser_Getipv4_addr(&parser, &addr_data)) { + BLog(BLOG_ERROR, "address missing for IPv4 address"); + return 0; + } + uint16_t port; + uint32_t addr; + memcpy(&port, port_data, sizeof(port)); + memcpy(&addr, addr_data, sizeof(addr)); + BAddr_InitIPv4(out_addr, addr, port); + } break; + case ADDR_TYPE_IPV6: { + uint8_t *port_data; + if (!addrParser_Getip_port(&parser, &port_data)) { + BLog(BLOG_ERROR, "port missing for IPv6 address"); + return 0; + } + uint8_t *addr_data; + if (!addrParser_Getipv6_addr(&parser, &addr_data)) { + BLog(BLOG_ERROR, "address missing for IPv6 address"); + return 0; + } + uint16_t port; + memcpy(&port, port_data, sizeof(port)); + BAddr_InitIPv6(out_addr, addr_data, port); + } break; + default: + BLog(BLOG_ERROR, "unknown address type %d", (int)type); + return 0; + } + + return 1; +} + +#endif diff --git a/app/src/main/jni/badvpn/protocol/dataproto.h b/app/src/main/jni/badvpn/protocol/dataproto.h new file mode 100644 index 0000000..998d953 --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/dataproto.h @@ -0,0 +1,91 @@ +/** + * @file dataproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for DataProto, the protocol for data transport between VPN peers. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A DataProto packet consists of: + * - the header (struct {@link dataproto_header}) + * - between zero and DATAPROTO_MAX_PEER_IDS destination peer IDs (struct {@link dataproto_peer_id}) + * - the payload, e.g. Ethernet frame + */ + +#ifndef BADVPN_PROTOCOL_DATAPROTO_H +#define BADVPN_PROTOCOL_DATAPROTO_H + +#include + +#include +#include + +#define DATAPROTO_MAX_PEER_IDS 1 + +#define DATAPROTO_FLAGS_RECEIVING_KEEPALIVES 1 + +/** + * DataProto header. + */ +B_START_PACKED +struct dataproto_header { + /** + * Bitwise OR of flags. Possible flags: + * - DATAPROTO_FLAGS_RECEIVING_KEEPALIVES + * Indicates that when the peer sent this packet, it has received at least + * one packet from the other peer in the last keep-alive tolerance time. + */ + uint8_t flags; + + /** + * ID of the peer this frame originates from. + */ + peerid_t from_id; + + /** + * Number of destination peer IDs that follow. + * Must be <=DATAPROTO_MAX_PEER_IDS. + */ + peerid_t num_peer_ids; +} B_PACKED; +B_END_PACKED + +/** + * Structure for a destination peer ID in DataProto. + * Wraps a single peerid_t in a packed struct for easy access. + */ +B_START_PACKED +struct dataproto_peer_id { + peerid_t id; +} B_PACKED; +B_END_PACKED + +#define DATAPROTO_MAX_OVERHEAD (sizeof(struct dataproto_header) + DATAPROTO_MAX_PEER_IDS * sizeof(struct dataproto_peer_id)) + +#endif diff --git a/app/src/main/jni/badvpn/protocol/fragmentproto.h b/app/src/main/jni/badvpn/protocol/fragmentproto.h new file mode 100644 index 0000000..4d2315e --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/fragmentproto.h @@ -0,0 +1,100 @@ +/** + * @file fragmentproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for FragmentProto, a protocol that allows sending of arbitrarily sized packets over + * a link with a fixed MTU. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A FragmentProto packet consists of a number of chunks. + * Each chunk consists of: + * - the chunk header (struct {@link fragmentproto_chunk_header}) + * - the chunk payload, i.e. part of the frame specified in the header + */ + +#ifndef BADVPN_PROTOCOL_FRAGMENTPROTO_H +#define BADVPN_PROTOCOL_FRAGMENTPROTO_H + +#include + +#include +#include + +typedef uint16_t fragmentproto_frameid; + +/** + * FragmentProto chunk header. + */ +B_START_PACKED +struct fragmentproto_chunk_header { + /** + * Identifier of the frame this chunk belongs to. + * Frames should be given ascending identifiers as they are encoded + * into chunks (except when the ID wraps to zero). + */ + fragmentproto_frameid frame_id; + + /** + * Position in the frame where this chunk starts. + */ + uint16_t chunk_start; + + /** + * Length of the chunk's payload. + */ + uint16_t chunk_len; + + /** + * Whether this is the last chunk of the frame, i.e. + * the total length of the frame is chunk_start + chunk_len. + */ + uint8_t is_last; +} B_PACKED; +B_END_PACKED + +/** + * Calculates how many chunks are needed at most for encoding one frame of the + * given maximum size with FragmentProto onto a carrier with a given MTU. + * This includes the case when the first chunk of a frame is not the first chunk + * in a FragmentProto packet. + * + * @param carrier_mtu MTU of the carrier, i.e. maximum length of FragmentProto packets. Must be >sizeof(struct fragmentproto_chunk_header). + * @param frame_mtu maximum frame size. Must be >=0. + * @return maximum number of chunks needed. Will be >0. + */ +static int fragmentproto_max_chunks_for_frame (int carrier_mtu, int frame_mtu) +{ + ASSERT(carrier_mtu > sizeof(struct fragmentproto_chunk_header)) + ASSERT(frame_mtu >= 0) + + return (bdivide_up(frame_mtu, (carrier_mtu - sizeof(struct fragmentproto_chunk_header))) + 1); +} + +#endif diff --git a/app/src/main/jni/badvpn/protocol/msgproto.bproto b/app/src/main/jni/badvpn/protocol/msgproto.bproto new file mode 100644 index 0000000..202931e --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/msgproto.bproto @@ -0,0 +1,43 @@ +// message for all MsgProto messages +message msg { + // message type, from msgproto.h + required uint16 type = 1; + // message payload. Is itself one of the messages below + // for "youconnect", "seed" and "confirmseed" messages, + // and empty for other messages + required data payload = 2; +}; + +// "youconnect" message payload +message msg_youconnect { + // external addresses to try; one or more msg_youconnect_addr messages + required repeated data addr = 1; + // encryption key if using UDP and encryption is enabled + optional data key = 2; + // password if using TCP + optional uint64 password = 3; +}; + +// an external address +message msg_youconnect_addr { + // scope name for this address + required data name = 1; + // address according to AddrProto + required data addr = 2; +}; + +// "seed" message payload +message msg_seed { + // identifier for the seed being send + required uint16 seed_id = 1; + // seed encryption key + required data key = 2; + // seed IV + required data iv = 3; +}; + +// "confirmseed" message payload +message msg_confirmseed { + // identifier for the seed being confirmed + required uint16 seed_id = 1; +}; diff --git a/app/src/main/jni/badvpn/protocol/msgproto.h b/app/src/main/jni/badvpn/protocol/msgproto.h new file mode 100644 index 0000000..286abb0 --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/msgproto.h @@ -0,0 +1,76 @@ +/** + * @file msgproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * MsgProto is used by each pair of VPN peers as messages through the server, in order to + * establish a direct data connection. MsgProto operates on top of the SCProto message + * service, optionally secured with SSL; see {@link scproto.h} for details. + * + * MsgProto is built with BProto, the protocol and code generator for building + * custom message protocols. The BProto specification file is msgproto.bproto. + * + * It goes roughly like that: + * + * We name one peer the master and the other the slave. The master is the one with + * greater ID. + * When the peers get to know about each other, the master starts the binding procedure. + * It binds/listens to an address, and sends the slave the "youconnect" message. It + * contains a list of external addresses for that bind address and additional parameters. + * Each external address includes a string called a scope name. The slave, which receives + * the "youconnect" message, finds the first external address whose scope it recognizes, + * and attempts to establish connection to that address. If it finds an address, buf fails + * at connecting, it sends "youretry", which makes the master restart the binding procedure + * after some time. If it however does not recognize any external address, it sends + * "cannotconnect" back to the master. + * When the master receives the "cannotconnect", it tries the next bind address, as described + * above. When the master runs out of bind addresses, it sends "cannotbind" to the slave. + * When the slave receives the "cannotbind", it starts its own binding procedure, similarly + * to what is described above, with master and slave reversed. First difference is if the + * master fails to connect to a recognized address, it doesn't send "youretry", but rather + * simply restarts the whole procedure after some time. The other difference is when the + * slave runs out of bind addresses, it not only sends "cannotbind" to the master, but + * registers relaying to the master. And in this case, when the master receives the "cannotbind", + * it doesn't start the binding procedure all all over, but registers relaying to the slave. + */ + +#ifndef BADVPN_PROTOCOL_MSGPROTO_H +#define BADVPN_PROTOCOL_MSGPROTO_H + +#include + +#define MSGID_YOUCONNECT 1 +#define MSGID_CANNOTCONNECT 2 +#define MSGID_CANNOTBIND 3 +#define MSGID_YOURETRY 5 +#define MSGID_SEED 6 +#define MSGID_CONFIRMSEED 7 + +#define MSG_MAX_PAYLOAD (SC_MAX_MSGLEN - msg_SIZEtype - msg_SIZEpayload(0)) + +#endif diff --git a/app/src/main/jni/badvpn/protocol/packetproto.h b/app/src/main/jni/badvpn/protocol/packetproto.h new file mode 100644 index 0000000..0f0982b --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/packetproto.h @@ -0,0 +1,68 @@ +/** + * @file packetproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for PacketProto, a protocol that allows sending of packets + * over a reliable stream connection. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * Packets are encoded into a stream by representing each packet with: + * - a 16-bit little-endian unsigned integer representing the length + * of the payload + * - that many bytes of payload + */ + +#ifndef BADVPN_PROTOCOL_PACKETPROTO_H +#define BADVPN_PROTOCOL_PACKETPROTO_H + +#include +#include + +#include + +/** + * PacketProto packet header. + * Wraps a single uint16_t in a packed struct for easy access. + */ +B_START_PACKED +struct packetproto_header +{ + /** + * Length of the packet payload that follows. + */ + uint16_t len; +} B_PACKED; +B_END_PACKED + +#define PACKETPROTO_ENCLEN(_len) (sizeof(struct packetproto_header) + (_len)) + +#define PACKETPROTO_MAXPAYLOAD UINT16_MAX + +#endif diff --git a/app/src/main/jni/badvpn/protocol/requestproto.h b/app/src/main/jni/badvpn/protocol/requestproto.h new file mode 100644 index 0000000..2ec3d0d --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/requestproto.h @@ -0,0 +1,50 @@ +/** + * @file requestproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_REQUESTPROTO_H +#define BADVPN_REQUESTPROTO_H + +#include + +#include + +#define REQUESTPROTO_TYPE_CLIENT_REQUEST 1 +#define REQUESTPROTO_TYPE_CLIENT_ABORT 2 +#define REQUESTPROTO_TYPE_SERVER_REPLY 3 +#define REQUESTPROTO_TYPE_SERVER_FINISHED 4 +#define REQUESTPROTO_TYPE_SERVER_ERROR 5 + +B_START_PACKED +struct requestproto_header { + uint32_t request_id; + uint32_t type; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/protocol/scproto.h b/app/src/main/jni/badvpn/protocol/scproto.h new file mode 100644 index 0000000..f138e0a --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/scproto.h @@ -0,0 +1,266 @@ +/** + * @file scproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for SCProto, the protocol that the clients communicate in + * with the server. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A SCProto packet consists of: + * - a header (struct {@link sc_header}) which contains the type of the + * packet + * - the payload + * + * It goes roughly like that: + * + * When the client connects to the server, it sends a "clienthello" packet + * to the server. The packet contains the protocol version the client is using. + * When the server receives the "clienthello" packet, it checks the version. + * If it doesn't match, it disconnects the client. Otherwise the server sends + * the client a "serverhello" packet to the client. That packet contains + * the ID of the client and possibly its IPv4 address as the server sees it + * (zero if not applicable). + * + * The server than proceeds to synchronize the peers' knowledge of each other. + * It does that by sending a "newclient" messages to a client to inform it of + * another peer, and "endclient" messages to inform it that a peer is gone. + * Each client, upon receiving a "newclient" message, MUST sent a corresponding + * "acceptpeer" message, before sending any messages to the new peer. + * The server forwards messages between synchronized peers to allow them to + * communicate. A peer sends a message to another peer by sending the "outmsg" + * packet to the server, and the server delivers a message to a peer by sending + * it the "inmsg" packet. + * + * The message service is reliable; messages from one client to another are + * expected to arrive unmodified and in the same order. There is, however, + * no flow control. This means that messages can not be used for bulk transfers + * between the clients (and they are not). If the server runs out of buffer for + * messages from one client to another, it will stop forwarding messages, and + * will reset knowledge of the two clients after some delay. Similarly, if one + * of the clients runs out of buffer locally, it will send the "resetpeer" + * packet to make the server reset knowledge. + * + * The messages transport either: + * + * - If the relevant "newclient" packets do not contain the + * SCID_NEWCLIENT_FLAG_SSL flag, then plaintext MsgProto messages. + * + * - If the relevant "newclient" packets do contain the SCID_NEWCLIENT_FLAG_SSL + * flag, then SSL, broken down into packets, PacketProto inside SSL, and finally + * MsgProto inside PacketProto. The master peer (one with higher ID) acts as an + * SSL server, and the other acts as an SSL client. The peers must identify with + * the same certificate they used when connecting to the server, and each peer + * must byte-compare the other's certificate agains the one provided to it by + * by the server in the relevent "newclient" message. + */ + +#ifndef BADVPN_PROTOCOL_SCPROTO_H +#define BADVPN_PROTOCOL_SCPROTO_H + +#include + +#include + +#define SC_VERSION 29 +#define SC_OLDVERSION_NOSSL 27 +#define SC_OLDVERSION_BROKENCERT 26 + +#define SC_KEEPALIVE_INTERVAL 10000 + +/** + * SCProto packet header. + * Follows up to SC_MAX_PAYLOAD bytes of payload. + */ +B_START_PACKED +struct sc_header { + /** + * Message type. + */ + uint8_t type; +} B_PACKED; +B_END_PACKED + +#define SC_MAX_PAYLOAD 2000 +#define SC_MAX_ENC (sizeof(struct sc_header) + SC_MAX_PAYLOAD) + +typedef uint16_t peerid_t; + +#define SCID_KEEPALIVE 0 +#define SCID_CLIENTHELLO 1 +#define SCID_SERVERHELLO 2 +#define SCID_NEWCLIENT 3 +#define SCID_ENDCLIENT 4 +#define SCID_OUTMSG 5 +#define SCID_INMSG 6 +#define SCID_RESETPEER 7 +#define SCID_ACCEPTPEER 8 + +/** + * "clienthello" client packet payload. + * Packet type is SCID_CLIENTHELLO. + */ +B_START_PACKED +struct sc_client_hello { + /** + * Protocol version the client is using. + */ + uint16_t version; +} B_PACKED; +B_END_PACKED + +/** + * "serverhello" server packet payload. + * Packet type is SCID_SERVERHELLO. + */ +B_START_PACKED +struct sc_server_hello { + /** + * Flags. Not used yet. + */ + uint16_t flags; + + /** + * Peer ID of the client. + */ + peerid_t id; + + /** + * IPv4 address of the client as seen by the server + * (network byte order). Zero if not applicable. + */ + uint32_t clientAddr; +} B_PACKED; +B_END_PACKED + +/** + * "newclient" server packet payload. + * Packet type is SCID_NEWCLIENT. + * If the server is using TLS, follows up to SCID_NEWCLIENT_MAX_CERT_LEN + * bytes of the new client's certificate (encoded in DER). + */ +B_START_PACKED +struct sc_server_newclient { + /** + * ID of the new peer. + */ + peerid_t id; + + /** + * Flags. Possible flags: + * - SCID_NEWCLIENT_FLAG_RELAY_SERVER + * You can relay frames to other peers through this peer. + * - SCID_NEWCLIENT_FLAG_RELAY_CLIENT + * You must allow this peer to relay frames to other peers through you. + * - SCID_NEWCLIENT_FLAG_SSL + * SSL must be used to talk to this peer through messages. + */ + uint16_t flags; +} B_PACKED; +B_END_PACKED + +#define SCID_NEWCLIENT_FLAG_RELAY_SERVER 1 +#define SCID_NEWCLIENT_FLAG_RELAY_CLIENT 2 +#define SCID_NEWCLIENT_FLAG_SSL 4 + +#define SCID_NEWCLIENT_MAX_CERT_LEN (SC_MAX_PAYLOAD - sizeof(struct sc_server_newclient)) + +/** + * "endclient" server packet payload. + * Packet type is SCID_ENDCLIENT. + */ +B_START_PACKED +struct sc_server_endclient { + /** + * ID of the removed peer. + */ + peerid_t id; +} B_PACKED; +B_END_PACKED + +/** + * "outmsg" client packet header. + * Packet type is SCID_OUTMSG. + * Follows up to SC_MAX_MSGLEN bytes of message payload. + */ +B_START_PACKED +struct sc_client_outmsg { + /** + * ID of the destionation peer. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +/** + * "inmsg" server packet payload. + * Packet type is SCID_INMSG. + * Follows up to SC_MAX_MSGLEN bytes of message payload. + */ +B_START_PACKED +struct sc_server_inmsg { + /** + * ID of the source peer. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +#define _SC_MAX_OUTMSGLEN (SC_MAX_PAYLOAD - sizeof(struct sc_client_outmsg)) +#define _SC_MAX_INMSGLEN (SC_MAX_PAYLOAD - sizeof(struct sc_server_inmsg)) + +#define SC_MAX_MSGLEN (_SC_MAX_OUTMSGLEN < _SC_MAX_INMSGLEN ? _SC_MAX_OUTMSGLEN : _SC_MAX_INMSGLEN) + +/** + * "resetpeer" client packet header. + * Packet type is SCID_RESETPEER. + */ +B_START_PACKED +struct sc_client_resetpeer { + /** + * ID of the peer to reset. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +/** + * "acceptpeer" client packet payload. + * Packet type is SCID_ACCEPTPEER. + */ +B_START_PACKED +struct sc_client_acceptpeer { + /** + * ID of the peer to accept. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/app/src/main/jni/badvpn/protocol/spproto.h b/app/src/main/jni/badvpn/protocol/spproto.h new file mode 100644 index 0000000..4b5bf5d --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/spproto.h @@ -0,0 +1,195 @@ +/** + * @file spproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Protocol for securing datagram communication. + * + * Security features implemented: + * - Encryption. Encrypts packets with a block cipher. + * Protects against a third party from seeing the data + * being transmitted. + * - Hashes. Adds a hash of the packet into the packet. + * Combined with encryption, protects against tampering + * with packets and crafting new packets. + * - One-time passwords. Adds a password to each packet + * for the receiver to recognize. Protects agains replaying + * packets and crafting new packets. + * + * A SPProto plaintext packet contains the following, in order: + * - if OTPs are used, a struct {@link spproto_otpdata} which contains + * the seed ID and the OTP, + * - if hashes are used, the hash, + * - payload data. + * + * If encryption is used: + * - the plaintext is padded by appending a 0x01 byte and as many 0x00 + * bytes as needed to align to block size, + * - the padded plaintext is encrypted, and + * - the initialization vector (IV) is prepended. + */ + +#ifndef BADVPN_PROTOCOL_SPPROTO_H +#define BADVPN_PROTOCOL_SPPROTO_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define SPPROTO_HASH_MODE_NONE 0 +#define SPPROTO_ENCRYPTION_MODE_NONE 0 +#define SPPROTO_OTP_MODE_NONE 0 + +/** + * Stores security parameters for SPProto. + */ +struct spproto_security_params { + /** + * Hash mode. + * Either SPPROTO_HASH_MODE_NONE for no hashes, or a valid bhash + * hash mode. + */ + int hash_mode; + + /** + * Encryption mode. + * Either SPPROTO_ENCRYPTION_MODE_NONE for no encryption, or a valid + * {@link BEncryption} cipher. + */ + int encryption_mode; + + /** + * One-time password (OTP) mode. + * Either SPPROTO_OTP_MODE_NONE for no OTPs, or a valid + * {@link BEncryption} cipher. + */ + int otp_mode; + + /** + * If OTPs are used (otp_mode != SPPROTO_OTP_MODE_NONE), number of + * OTPs generated from a single seed. + */ + int otp_num; +}; + +#define SPPROTO_HAVE_HASH(_params) ((_params).hash_mode != SPPROTO_HASH_MODE_NONE) +#define SPPROTO_HASH_SIZE(_params) ( \ + SPPROTO_HAVE_HASH(_params) ? \ + BHash_size((_params).hash_mode) : \ + 0 \ +) + +#define SPPROTO_HAVE_ENCRYPTION(_params) ((_params).encryption_mode != SPPROTO_ENCRYPTION_MODE_NONE) + +#define SPPROTO_HAVE_OTP(_params) ((_params).otp_mode != SPPROTO_OTP_MODE_NONE) + +B_START_PACKED +struct spproto_otpdata { + uint16_t seed_id; + otp_t otp; +} B_PACKED; +B_END_PACKED + +#define SPPROTO_HEADER_OTPDATA_OFF(_params) 0 +#define SPPROTO_HEADER_OTPDATA_LEN(_params) (SPPROTO_HAVE_OTP(_params) ? sizeof(struct spproto_otpdata) : 0) +#define SPPROTO_HEADER_HASH_OFF(_params) (SPPROTO_HEADER_OTPDATA_OFF(_params) + SPPROTO_HEADER_OTPDATA_LEN(_params)) +#define SPPROTO_HEADER_HASH_LEN(_params) SPPROTO_HASH_SIZE(_params) +#define SPPROTO_HEADER_LEN(_params) (SPPROTO_HEADER_HASH_OFF(_params) + SPPROTO_HEADER_HASH_LEN(_params)) + +/** + * Asserts that the given SPProto security parameters are valid. + * + * @param params security parameters + */ +static void spproto_assert_security_params (struct spproto_security_params params) +{ + ASSERT(params.hash_mode == SPPROTO_HASH_MODE_NONE || BHash_type_valid(params.hash_mode)) + ASSERT(params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE || BEncryption_cipher_valid(params.encryption_mode)) + ASSERT(params.otp_mode == SPPROTO_OTP_MODE_NONE || BEncryption_cipher_valid(params.otp_mode)) + ASSERT(params.otp_mode == SPPROTO_OTP_MODE_NONE || params.otp_num > 0) +} + +/** + * Calculates the maximum payload size for SPProto given the + * security parameters and the maximum encoded packet size. + * + * @param params security parameters + * @param carrier_mtu maximum encoded packet size. Must be >=0. + * @return maximum payload size. Negative means is is impossible + * to encode anything. + */ +static int spproto_payload_mtu_for_carrier_mtu (struct spproto_security_params params, int carrier_mtu) +{ + spproto_assert_security_params(params); + ASSERT(carrier_mtu >= 0) + + if (params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE) { + return (carrier_mtu - SPPROTO_HEADER_LEN(params)); + } else { + int block_size = BEncryption_cipher_block_size(params.encryption_mode); + return (balign_down(carrier_mtu, block_size) - block_size - SPPROTO_HEADER_LEN(params) - 1); + } +} + +/** + * Calculates the maximum encoded packet size for SPProto given the + * security parameters and the maximum payload size. + * + * @param params security parameters + * @param payload_mtu maximum payload size. Must be >=0. + * @return maximum encoded packet size, -1 if payload_mtu is too large + */ +static int spproto_carrier_mtu_for_payload_mtu (struct spproto_security_params params, int payload_mtu) +{ + spproto_assert_security_params(params); + ASSERT(payload_mtu >= 0) + + if (params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE) { + if (payload_mtu > INT_MAX - SPPROTO_HEADER_LEN(params)) { + return -1; + } + + return (SPPROTO_HEADER_LEN(params) + payload_mtu); + } else { + int block_size = BEncryption_cipher_block_size(params.encryption_mode); + + if (payload_mtu > INT_MAX - (block_size + SPPROTO_HEADER_LEN(params) + block_size)) { + return -1; + } + + return (block_size + balign_up((SPPROTO_HEADER_LEN(params) + payload_mtu + 1), block_size)); + } +} + +#endif diff --git a/app/src/main/jni/badvpn/protocol/udpgw_proto.h b/app/src/main/jni/badvpn/protocol/udpgw_proto.h new file mode 100644 index 0000000..da5f02d --- /dev/null +++ b/app/src/main/jni/badvpn/protocol/udpgw_proto.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PROTOCOL_UDPGW_PROTO_H +#define BADVPN_PROTOCOL_UDPGW_PROTO_H + +#include + +#include +#include + +#define UDPGW_CLIENT_FLAG_KEEPALIVE (1 << 0) +#define UDPGW_CLIENT_FLAG_REBIND (1 << 1) +#define UDPGW_CLIENT_FLAG_DNS (1 << 2) +#define UDPGW_CLIENT_FLAG_IPV6 (1 << 3) + +#ifdef ANDROID +B_START_PACKED +struct socks_udp_header { + uint16_t rsv; + uint8_t frag; + uint8_t atyp; +} B_PACKED; +B_END_PACKED +#endif + +B_START_PACKED +struct udpgw_header { + uint8_t flags; + uint16_t conid; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv4 { + uint32_t addr_ip; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv6 { + uint8_t addr_ip[16]; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +static int udpgw_compute_mtu (int dgram_mtu) +{ + bsize_t bs = bsize_add( +#ifdef ANDROID + bsize_fromsize(sizeof(struct socks_udp_header)), +#else + bsize_fromsize(sizeof(struct udpgw_header)), +#endif + bsize_add( + bsize_max( + bsize_fromsize(sizeof(struct udpgw_addr_ipv4)), + bsize_fromsize(sizeof(struct udpgw_addr_ipv6)) + ), + bsize_fromint(dgram_mtu) + ) + ); + + int s; + if (!bsize_toint(bs, &s)) { + return -1; + } + + return s; +} + +#endif diff --git a/app/src/main/jni/badvpn/random/BRandom2.c b/app/src/main/jni/badvpn/random/BRandom2.c new file mode 100644 index 0000000..a0761de --- /dev/null +++ b/app/src/main/jni/badvpn/random/BRandom2.c @@ -0,0 +1,90 @@ +/** + * @file BRandom2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "BRandom2.h" + +static int do_init (BRandom2 *o) +{ + if (o->initialized) { + return 1; + } + + o->urandom_fd = open("/dev/urandom", O_RDONLY); + if (o->urandom_fd < 0) { + return 0; + } + + o->initialized = 1; + + return 1; +} + +int BRandom2_Init (BRandom2 *o, int flags) +{ + ASSERT((flags & ~(BRANDOM2_INIT_LAZY)) == 0) + + o->initialized = 0; + + if (!(flags & BRANDOM2_INIT_LAZY) && !do_init(o)) { + return 0; + } + + DebugObject_Init(&o->d_obj); + return 1; +} + +void BRandom2_Free (BRandom2 *o) +{ + DebugObject_Free(&o->d_obj); + + if (o->initialized) { + close(o->urandom_fd); + } +} + +int BRandom2_GenBytes (BRandom2 *o, void *out, size_t len) +{ + DebugObject_Access(&o->d_obj); + + if (!do_init(o)) { + return 0; + } + + ssize_t res = read(o->urandom_fd, out, len); + if (res < 0 || res != len) { + return 0; + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/random/BRandom2.h b/app/src/main/jni/badvpn/random/BRandom2.h new file mode 100644 index 0000000..6851b84 --- /dev/null +++ b/app/src/main/jni/badvpn/random/BRandom2.h @@ -0,0 +1,50 @@ +/** + * @file BRandom2.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_RANDOM2_H +#define BADVPN_RANDOM2_H + +#include + +#include +#include + +#define BRANDOM2_INIT_LAZY (1 << 0) + +typedef struct { + int initialized; + int urandom_fd; + DebugObject d_obj; +} BRandom2; + +int BRandom2_Init (BRandom2 *o, int flags) WARN_UNUSED; +void BRandom2_Free (BRandom2 *o); +int BRandom2_GenBytes (BRandom2 *o, void *out, size_t len) WARN_UNUSED; + +#endif diff --git a/app/src/main/jni/badvpn/random/CMakeLists.txt b/app/src/main/jni/badvpn/random/CMakeLists.txt new file mode 100644 index 0000000..3b958d6 --- /dev/null +++ b/app/src/main/jni/badvpn/random/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(badvpn_random + BRandom2.c +) +target_link_libraries(badvpn_random base) diff --git a/app/src/main/jni/badvpn/scripts/cmake b/app/src/main/jni/badvpn/scripts/cmake new file mode 100644 index 0000000..51af777 --- /dev/null +++ b/app/src/main/jni/badvpn/scripts/cmake @@ -0,0 +1,8 @@ +#!/bin/sh + +export ROOT="" +export MINGW="/home//mingw/cross_win32" + +export PATH="$MINGW/bin:$PATH" + +exec /usr/bin/cmake -DCMAKE_TOOLCHAIN_FILE="$ROOT/toolchain.cmake" "$@" diff --git a/app/src/main/jni/badvpn/scripts/copy_nss b/app/src/main/jni/badvpn/scripts/copy_nss new file mode 100644 index 0000000..9b52112 --- /dev/null +++ b/app/src/main/jni/badvpn/scripts/copy_nss @@ -0,0 +1,23 @@ +#!/bin/sh + +NSSDIST="$1" +DEST="$2" + +if [ -z "${NSSDIST}" ] || [ -z "${DEST}" ]; then + echo "Copies a Windows build of NSS such that it can be found by the BadVPN build system" + echo "Usage: $0 " + exit 1 +fi + +NSSOBJ="${NSSDIST}/WINNT5.1_OPT.OBJ" + +set -e + +mkdir -p "${DEST}"/include +cp -r "${NSSOBJ}/include"/* "${DEST}"/include/ +cp -r "${NSSDIST}/public/nss"/* "${DEST}"/include/ +mkdir -p "${DEST}"/lib +cp "${NSSOBJ}/lib"/{libnspr4,libplc4,libplds4,ssl3,smime3,nss3}.lib "${DEST}"/lib/ +mkdir -p "${DEST}"/bin +cp "${NSSOBJ}/lib"/*.dll "${DEST}"/bin/ +cp "${NSSOBJ}/bin"/*.exe "${DEST}"/bin/ diff --git a/app/src/main/jni/badvpn/scripts/toolchain.cmake b/app/src/main/jni/badvpn/scripts/toolchain.cmake new file mode 100644 index 0000000..5f4a90a --- /dev/null +++ b/app/src/main/jni/badvpn/scripts/toolchain.cmake @@ -0,0 +1,6 @@ +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +SET(CMAKE_FIND_ROOT_PATH $ENV{ROOT}) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/app/src/main/jni/badvpn/security/BEncryption.c b/app/src/main/jni/badvpn/security/BEncryption.c new file mode 100644 index 0000000..f0f476c --- /dev/null +++ b/app/src/main/jni/badvpn/security/BEncryption.c @@ -0,0 +1,240 @@ +/** + * @file BEncryption.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +int BEncryption_cipher_valid (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + case BENCRYPTION_CIPHER_AES: + return 1; + default: + return 0; + } +} + +int BEncryption_cipher_block_size (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + return BENCRYPTION_CIPHER_BLOWFISH_BLOCK_SIZE; + case BENCRYPTION_CIPHER_AES: + return BENCRYPTION_CIPHER_AES_BLOCK_SIZE; + default: + ASSERT(0) + return 0; + } +} + +int BEncryption_cipher_key_size (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + return BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE; + case BENCRYPTION_CIPHER_AES: + return BENCRYPTION_CIPHER_AES_KEY_SIZE; + default: + ASSERT(0) + return 0; + } +} + +void BEncryption_Init (BEncryption *enc, int mode, int cipher, uint8_t *key) +{ + ASSERT(!(mode&~(BENCRYPTION_MODE_ENCRYPT|BENCRYPTION_MODE_DECRYPT))) + ASSERT((mode&BENCRYPTION_MODE_ENCRYPT) || (mode&BENCRYPTION_MODE_DECRYPT)) + + enc->mode = mode; + enc->cipher = cipher; + + #ifdef BADVPN_USE_CRYPTODEV + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_AES: + enc->cryptodev.cipher = CRYPTO_AES_CBC; + break; + default: + goto fail1; + } + + if ((enc->cryptodev.fd = open("/dev/crypto", O_RDWR, 0)) < 0) { + BLog(BLOG_ERROR, "failed to open /dev/crypto"); + goto fail1; + } + + if (ioctl(enc->cryptodev.fd, CRIOGET, &enc->cryptodev.cfd)) { + BLog(BLOG_ERROR, "failed ioctl(CRIOGET)"); + goto fail2; + } + + struct session_op sess; + memset(&sess, 0, sizeof(sess)); + sess.cipher = enc->cryptodev.cipher; + sess.keylen = BEncryption_cipher_key_size(enc->cipher); + sess.key = key; + if (ioctl(enc->cryptodev.cfd, CIOCGSESSION, &sess)) { + BLog(BLOG_ERROR, "failed ioctl(CIOCGSESSION)"); + goto fail3; + } + + enc->cryptodev.ses = sess.ses; + enc->use_cryptodev = 1; + + goto success; + +fail3: + ASSERT_FORCE(close(enc->cryptodev.cfd) == 0) +fail2: + ASSERT_FORCE(close(enc->cryptodev.fd) == 0) +fail1: + + enc->use_cryptodev = 0; + + #endif + + int res; + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_set_key(&enc->blowfish, BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE, key); + break; + case BENCRYPTION_CIPHER_AES: + if (enc->mode&BENCRYPTION_MODE_ENCRYPT) { + res = AES_set_encrypt_key(key, 128, &enc->aes.encrypt); + ASSERT_EXECUTE(res >= 0) + } + if (enc->mode&BENCRYPTION_MODE_DECRYPT) { + res = AES_set_decrypt_key(key, 128, &enc->aes.decrypt); + ASSERT_EXECUTE(res >= 0) + } + break; + default: + ASSERT(0) + ; + } + + #ifdef BADVPN_USE_CRYPTODEV +success: + #endif + // init debug object + DebugObject_Init(&enc->d_obj); +} + +void BEncryption_Free (BEncryption *enc) +{ + // free debug object + DebugObject_Free(&enc->d_obj); + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCFSESSION, &enc->cryptodev.ses) == 0) + ASSERT_FORCE(close(enc->cryptodev.cfd) == 0) + ASSERT_FORCE(close(enc->cryptodev.fd) == 0) + } + + #endif +} + +void BEncryption_Encrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv) +{ + ASSERT(enc->mode&BENCRYPTION_MODE_ENCRYPT) + ASSERT(len >= 0) + ASSERT(len % BEncryption_cipher_block_size(enc->cipher) == 0) + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + struct crypt_op cryp; + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = enc->cryptodev.ses; + cryp.len = len; + cryp.src = in; + cryp.dst = out; + cryp.iv = iv; + cryp.op = COP_ENCRYPT; + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCCRYPT, &cryp) == 0) + + return; + } + + #endif + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_cbc_encrypt(in, out, len, &enc->blowfish, iv, BF_ENCRYPT); + break; + case BENCRYPTION_CIPHER_AES: + AES_cbc_encrypt(in, out, len, &enc->aes.encrypt, iv, AES_ENCRYPT); + break; + default: + ASSERT(0); + } +} + +void BEncryption_Decrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv) +{ + ASSERT(enc->mode&BENCRYPTION_MODE_DECRYPT) + ASSERT(len >= 0) + ASSERT(len % BEncryption_cipher_block_size(enc->cipher) == 0) + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + struct crypt_op cryp; + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = enc->cryptodev.ses; + cryp.len = len; + cryp.src = in; + cryp.dst = out; + cryp.iv = iv; + cryp.op = COP_DECRYPT; + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCCRYPT, &cryp) == 0) + + return; + } + + #endif + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_cbc_encrypt(in, out, len, &enc->blowfish, iv, BF_DECRYPT); + break; + case BENCRYPTION_CIPHER_AES: + AES_cbc_encrypt(in, out, len, &enc->aes.decrypt, iv, AES_DECRYPT); + break; + default: + ASSERT(0); + } +} diff --git a/app/src/main/jni/badvpn/security/BEncryption.h b/app/src/main/jni/badvpn/security/BEncryption.h new file mode 100644 index 0000000..60a4fdc --- /dev/null +++ b/app/src/main/jni/badvpn/security/BEncryption.h @@ -0,0 +1,175 @@ +/** + * @file BEncryption.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Block cipher encryption abstraction. + */ + +#ifndef BADVPN_SECURITY_BENCRYPTION_H +#define BADVPN_SECURITY_BENCRYPTION_H + +#include +#include + +#ifdef BADVPN_USE_CRYPTODEV +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#define BENCRYPTION_MODE_ENCRYPT 1 +#define BENCRYPTION_MODE_DECRYPT 2 + +#define BENCRYPTION_MAX_BLOCK_SIZE 16 +#define BENCRYPTION_MAX_KEY_SIZE 16 + +#define BENCRYPTION_CIPHER_BLOWFISH 1 +#define BENCRYPTION_CIPHER_BLOWFISH_BLOCK_SIZE 8 +#define BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE 16 + +#define BENCRYPTION_CIPHER_AES 2 +#define BENCRYPTION_CIPHER_AES_BLOCK_SIZE 16 +#define BENCRYPTION_CIPHER_AES_KEY_SIZE 16 + +// NOTE: update the maximums above when adding a cipher! + +/** + * Block cipher encryption abstraction. + */ +typedef struct { + DebugObject d_obj; + int mode; + int cipher; + #ifdef BADVPN_USE_CRYPTODEV + int use_cryptodev; + #endif + union { + BF_KEY blowfish; + struct { + AES_KEY encrypt; + AES_KEY decrypt; + } aes; + #ifdef BADVPN_USE_CRYPTODEV + struct { + int fd; + int cfd; + int cipher; + uint32_t ses; + } cryptodev; + #endif + }; +} BEncryption; + +/** + * Checks if the given cipher number is valid. + * + * @param cipher cipher number + * @return 1 if valid, 0 if not + */ +int BEncryption_cipher_valid (int cipher); + +/** + * Returns the block size of a cipher. + * + * @param cipher cipher number. Must be valid. + * @return block size in bytes + */ +int BEncryption_cipher_block_size (int cipher); + +/** + * Returns the key size of a cipher. + * + * @param cipher cipher number. Must be valid. + * @return key size in bytes + */ +int BEncryption_cipher_key_size (int cipher); + +/** + * Initializes the object. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this object + * will be used from a non-main thread. + * + * @param enc the object + * @param mode whether encryption or decryption is to be done, or both. + * Must be a bitwise-OR of at least one of BENCRYPTION_MODE_ENCRYPT + * and BENCRYPTION_MODE_DECRYPT. + * @param cipher cipher number. Must be valid. + * @param key encryption key + */ +void BEncryption_Init (BEncryption *enc, int mode, int cipher, uint8_t *key); + +/** + * Frees the object. + * + * @param enc the object + */ +void BEncryption_Free (BEncryption *enc); + +/** + * Encrypts data. + * The object must have been initialized with mode including + * BENCRYPTION_MODE_ENCRYPT. + * + * @param enc the object + * @param in data to encrypt + * @param out ciphertext output + * @param len number of bytes to encrypt. Must be >=0 and a multiple of + * block size. + * @param iv initialization vector. Updated such that continuing a previous encryption + * starting with the updated IV is equivalent to performing just one encryption. + */ +void BEncryption_Encrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv); + +/** + * Decrypts data. + * The object must have been initialized with mode including + * BENCRYPTION_MODE_DECRYPT. + * + * @param enc the object + * @param in data to decrypt + * @param out plaintext output + * @param len number of bytes to decrypt. Must be >=0 and a multiple of + * block size. + * @param iv initialization vector. Updated such that continuing a previous decryption + * starting with the updated IV is equivalent to performing just one decryption. + */ +void BEncryption_Decrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv); + +#endif diff --git a/app/src/main/jni/badvpn/security/BHash.c b/app/src/main/jni/badvpn/security/BHash.c new file mode 100644 index 0000000..fec2616 --- /dev/null +++ b/app/src/main/jni/badvpn/security/BHash.c @@ -0,0 +1,69 @@ +/** + * @file BHash.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int BHash_type_valid (int type) +{ + switch (type) { + case BHASH_TYPE_MD5: + case BHASH_TYPE_SHA1: + return 1; + default: + return 0; + } +} + +int BHash_size (int type) +{ + switch (type) { + case BHASH_TYPE_MD5: + return BHASH_TYPE_MD5_SIZE; + case BHASH_TYPE_SHA1: + return BHASH_TYPE_SHA1_SIZE; + default: + ASSERT(0) + return 0; + } +} + +void BHash_calculate (int type, uint8_t *data, int data_len, uint8_t *out) +{ + switch (type) { + case BHASH_TYPE_MD5: + MD5(data, data_len, out); + break; + case BHASH_TYPE_SHA1: + SHA1(data, data_len, out); + break; + default: + ASSERT(0) + ; + } +} diff --git a/app/src/main/jni/badvpn/security/BHash.h b/app/src/main/jni/badvpn/security/BHash.h new file mode 100644 index 0000000..6fb6f9a --- /dev/null +++ b/app/src/main/jni/badvpn/security/BHash.h @@ -0,0 +1,80 @@ +/** + * @file BHash.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Cryptographic hash funtions abstraction. + */ + +#ifndef BADVPN_SECURITY_BHASH_H +#define BADVPN_SECURITY_BHASH_H + +#include + +#include +#include + +#include + +#define BHASH_TYPE_MD5 1 +#define BHASH_TYPE_MD5_SIZE 16 + +#define BHASH_TYPE_SHA1 2 +#define BHASH_TYPE_SHA1_SIZE 20 + +#define BHASH_MAX_SIZE 20 + +/** + * Checks if the given hash type number is valid. + * + * @param type hash type number + * @return 1 if valid, 0 if not + */ +int BHash_type_valid (int type); + +/** + * Returns the size of a hash. + * + * @param cipher hash type number. Must be valid. + * @return hash size in bytes + */ +int BHash_size (int type); + +/** + * Calculates a hash. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this is + * being called from a non-main thread. + * + * @param type hash type number. Must be valid. + * @param data data to calculate the hash of + * @param data_len length of data + * @param out the hash will be written here. Must not overlap with data. + */ +void BHash_calculate (int type, uint8_t *data, int data_len, uint8_t *out); + +#endif diff --git a/app/src/main/jni/badvpn/security/BRandom.c b/app/src/main/jni/badvpn/security/BRandom.c new file mode 100644 index 0000000..ea9f276 --- /dev/null +++ b/app/src/main/jni/badvpn/security/BRandom.c @@ -0,0 +1,42 @@ +/** + * @file BRandom.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +void BRandom_randomize (uint8_t *buf, int len) +{ + ASSERT(len >= 0) + + DEBUG_ZERO_MEMORY(buf, len) + ASSERT_FORCE(RAND_bytes(buf, len) == 1) +} diff --git a/app/src/main/jni/badvpn/security/BRandom.h b/app/src/main/jni/badvpn/security/BRandom.h new file mode 100644 index 0000000..3529b82 --- /dev/null +++ b/app/src/main/jni/badvpn/security/BRandom.h @@ -0,0 +1,49 @@ +/** + * @file BRandom.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Random data generation function. + */ + +#ifndef BADVPN_SECURITY_BRANDOM_H +#define BADVPN_SECURITY_BRANDOM_H + +#include + +/** + * Generates random data. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this is + * being called from a non-main thread. + * + * @param buf buffer to write data into + * @param len number of bytes to generate. Must be >=0. + */ +void BRandom_randomize (uint8_t *buf, int len); + +#endif diff --git a/app/src/main/jni/badvpn/security/BSecurity.c b/app/src/main/jni/badvpn/security/BSecurity.c new file mode 100644 index 0000000..f3fb7c8 --- /dev/null +++ b/app/src/main/jni/badvpn/security/BSecurity.c @@ -0,0 +1,149 @@ +/** + * @file BSecurity.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include +#endif + +#include + +#include +#include + +#include + +int bsecurity_initialized = 0; + +#ifdef BADVPN_THREADWORK_USE_PTHREAD +pthread_mutex_t *bsecurity_locks; +int bsecurity_num_locks; +#endif + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + +static unsigned long id_callback (void) +{ + ASSERT(bsecurity_initialized) + + return (unsigned long)pthread_self(); +} + +static void locking_callback (int mode, int type, const char *file, int line) +{ + ASSERT(bsecurity_initialized) + ASSERT(type >= 0) + ASSERT(type < bsecurity_num_locks) + + if ((mode & CRYPTO_LOCK)) { + ASSERT_FORCE(pthread_mutex_lock(&bsecurity_locks[type]) == 0) + } else { + ASSERT_FORCE(pthread_mutex_unlock(&bsecurity_locks[type]) == 0) + } +} + +#endif + +int BSecurity_GlobalInitThreadSafe (void) +{ + ASSERT(!bsecurity_initialized) + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + // get number of locks + int num_locks = CRYPTO_num_locks(); + ASSERT_FORCE(num_locks >= 0) + + // alloc locks array + if (!(bsecurity_locks = BAllocArray(num_locks, sizeof(bsecurity_locks[0])))) { + goto fail0; + } + + // init locks + bsecurity_num_locks = 0; + for (int i = 0; i < num_locks; i++) { + if (pthread_mutex_init(&bsecurity_locks[i], NULL) != 0) { + goto fail1; + } + bsecurity_num_locks++; + } + + #endif + + bsecurity_initialized = 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + CRYPTO_set_id_callback(id_callback); + CRYPTO_set_locking_callback(locking_callback); + #endif + + return 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD +fail1: + while (bsecurity_num_locks > 0) { + ASSERT_FORCE(pthread_mutex_destroy(&bsecurity_locks[bsecurity_num_locks - 1]) == 0) + bsecurity_num_locks--; + } + BFree(bsecurity_locks); +fail0: + return 0; + #endif +} + +void BSecurity_GlobalFreeThreadSafe (void) +{ + ASSERT(bsecurity_initialized) + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + // remove callbacks + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + // free locks + while (bsecurity_num_locks > 0) { + ASSERT_FORCE(pthread_mutex_destroy(&bsecurity_locks[bsecurity_num_locks - 1]) == 0) + bsecurity_num_locks--; + } + + // free locks array + BFree(bsecurity_locks); + + #endif + + bsecurity_initialized = 0; +} + +void BSecurity_GlobalAssertThreadSafe (int thread_safe) +{ + ASSERT(thread_safe == 0 || thread_safe == 1) + ASSERT(!(thread_safe) || bsecurity_initialized) +} diff --git a/app/src/main/jni/badvpn/security/BSecurity.h b/app/src/main/jni/badvpn/security/BSecurity.h new file mode 100644 index 0000000..c429e4e --- /dev/null +++ b/app/src/main/jni/badvpn/security/BSecurity.h @@ -0,0 +1,60 @@ +/** + * @file BSecurity.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Initialization of OpenSSL for security functions. + */ + +#ifndef BADVPN_SECURITY_BSECURITY_H +#define BADVPN_SECURITY_BSECURITY_H + +/** + * Initializes thread safety for security functions. + * Thread safety must not be initialized. + * + * @return 1 on success, 0 on failure + */ +int BSecurity_GlobalInitThreadSafe (void); + +/** + * Deinitializes thread safety for security functions. + * Thread safety must be initialized. + */ +void BSecurity_GlobalFreeThreadSafe (void); + +/** + * Asserts that {@link BSecurity_GlobalInitThreadSafe} was done, + * if thread_safe=1. + * + * @param thread_safe whether thread safety is to be asserted. + * Must be 0 or 1. + */ +void BSecurity_GlobalAssertThreadSafe (int thread_safe); + +#endif diff --git a/app/src/main/jni/badvpn/security/CMakeLists.txt b/app/src/main/jni/badvpn/security/CMakeLists.txt new file mode 100644 index 0000000..773cb4f --- /dev/null +++ b/app/src/main/jni/badvpn/security/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(security + BSecurity.c + BEncryption.c + BHash.c + BRandom.c + OTPCalculator.c + OTPChecker.c + OTPGenerator.c +) +target_link_libraries(security system threadwork ${LIBCRYPTO_LIBRARIES}) diff --git a/app/src/main/jni/badvpn/security/OTPCalculator.c b/app/src/main/jni/badvpn/security/OTPCalculator.c new file mode 100644 index 0000000..069dbe6 --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPCalculator.c @@ -0,0 +1,118 @@ +/** + * @file OTPCalculator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +int OTPCalculator_Init (OTPCalculator *calc, int num_otps, int cipher) +{ + ASSERT(num_otps >= 0) + ASSERT(BEncryption_cipher_valid(cipher)) + + // init arguments + calc->num_otps = num_otps; + calc->cipher = cipher; + + // remember block size + calc->block_size = BEncryption_cipher_block_size(calc->cipher); + + // calculate number of blocks + if (calc->num_otps > SIZE_MAX / sizeof(otp_t)) { + goto fail0; + } + calc->num_blocks = bdivide_up(calc->num_otps * sizeof(otp_t), calc->block_size); + + // allocate buffer + if (!(calc->data = (otp_t *)BAllocArray(calc->num_blocks, calc->block_size))) { + goto fail0; + } + + // init debug object + DebugObject_Init(&calc->d_obj); + + return 1; + +fail0: + return 0; +} + +void OTPCalculator_Free (OTPCalculator *calc) +{ + // free debug object + DebugObject_Free(&calc->d_obj); + + // free buffer + BFree(calc->data); +} + +otp_t * OTPCalculator_Generate (OTPCalculator *calc, uint8_t *key, uint8_t *iv, int shuffle) +{ + ASSERT(shuffle == 0 || shuffle == 1) + + // copy IV so it can be updated + uint8_t iv_work[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv_work, iv, calc->block_size); + + // create zero block + uint8_t zero[BENCRYPTION_MAX_BLOCK_SIZE]; + memset(zero, 0, calc->block_size); + + // init encryptor + BEncryption encryptor; + BEncryption_Init(&encryptor, BENCRYPTION_MODE_ENCRYPT, calc->cipher, key); + + // encrypt zero blocks + for (size_t i = 0; i < calc->num_blocks; i++) { + BEncryption_Encrypt(&encryptor, zero, (uint8_t *)calc->data + i * calc->block_size, calc->block_size, iv_work); + } + + // free encryptor + BEncryption_Free(&encryptor); + + // shuffle if requested + if (shuffle) { + int i = 0; + while (i < calc->num_otps) { + uint16_t ints[256]; + BRandom_randomize((uint8_t *)ints, sizeof(ints)); + for (int j = 0; j < 256 && i < calc->num_otps; j++) { + int newIndex = i + (ints[j] % (calc->num_otps - i)); + otp_t temp = calc->data[i]; + calc->data[i] = calc->data[newIndex]; + calc->data[newIndex] = temp; + i++; + } + } + } + + return calc->data; +} diff --git a/app/src/main/jni/badvpn/security/OTPCalculator.h b/app/src/main/jni/badvpn/security/OTPCalculator.h new file mode 100644 index 0000000..f15f845 --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPCalculator.h @@ -0,0 +1,96 @@ +/** + * @file OTPCalculator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that calculates OTPs. + */ + +#ifndef BADVPN_SECURITY_OTPCALCULATOR_H +#define BADVPN_SECURITY_OTPCALCULATOR_H + +#include +#include + +#include +#include +#include +#include +#include + +/** + * Type for an OTP. + */ +typedef uint32_t otp_t; + +/** + * Object that calculates OTPs. + */ +typedef struct { + DebugObject d_obj; + int num_otps; + int cipher; + int block_size; + size_t num_blocks; + otp_t *data; +} OTPCalculator; + +/** + * Initializes the calculator. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this object + * will be used from a non-main thread. + * + * @param calc the object + * @param num_otps number of OTPs to generate from a seed. Must be >=0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @return 1 on success, 0 on failure + */ +int OTPCalculator_Init (OTPCalculator *calc, int num_otps, int cipher) WARN_UNUSED; + +/** + * Frees the calculator. + * + * @param calc the object + */ +void OTPCalculator_Free (OTPCalculator *calc); + +/** + * Generates OTPs from the given key and IV. + * + * @param calc the object + * @param key encryption key + * @param iv initialization vector + * @param shuffle whether to shuffle the OTPs. Must be 1 or 0. + * @return pointer to an array of 32-bit OPTs. Constains as many OTPs as was specified + * in {@link OTPCalculator_Init}. Valid until the next generation or + * until the object is freed. + */ +otp_t * OTPCalculator_Generate (OTPCalculator *calc, uint8_t *key, uint8_t *iv, int shuffle); + +#endif diff --git a/app/src/main/jni/badvpn/security/OTPChecker.c b/app/src/main/jni/badvpn/security/OTPChecker.c new file mode 100644 index 0000000..8606a31 --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPChecker.c @@ -0,0 +1,297 @@ +/** + * @file OTPChecker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t); +static void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp); +static void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv); +static int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp); + +void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t) +{ + for (int i = 0; i < mc->num_entries; i++) { + t->entries[i].avail = -1; + } +} + +void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp) +{ + // calculate starting index + int start_index = otp % mc->num_entries; + + // try indexes starting with the base position + for (int i = 0; i < mc->num_entries; i++) { + int index = bmodadd_int(start_index, i, mc->num_entries); + struct OTPChecker_entry *entry = &t->entries[index]; + + // if we find a free index, use it + if (entry->avail < 0) { + entry->otp = otp; + entry->avail = 1; + return; + } + + // if we find a used index with the same mac, + // use it by incrementing its count + if (entry->otp == otp) { + entry->avail++; + return; + } + } + + // will never add more macs than we can hold + ASSERT(0) +} + +void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv) +{ + // calculate values + otp_t *otps = OTPCalculator_Generate(calc, key, iv, 0); + + // empty table + OTPChecker_Table_Empty(mc ,t); + + // add calculated values to table + for (int i = 0; i < mc->num_otps; i++) { + OTPChecker_Table_AddOTP(mc, t, otps[i]); + } +} + +int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp) +{ + // calculate starting index + int start_index = otp % mc->num_entries; + + // try indexes starting with the base position + for (int i = 0; i < mc->num_entries; i++) { + int index = bmodadd_int(start_index, i, mc->num_entries); + struct OTPChecker_entry *entry = &t->entries[index]; + + // if we find an empty entry, there is no such mac + if (entry->avail < 0) { + return 0; + } + + // if we find a matching entry, check its count + if (entry->otp == otp) { + if (entry->avail > 0) { + entry->avail--; + return 1; + } + return 0; + } + } + + // there are always empty slots + ASSERT(0) + return 0; +} + +static void work_func (OTPChecker *mc) +{ + struct OTPChecker_table *table = &mc->tables[mc->next_table]; + OTPChecker_Table_Generate(mc, table, &mc->calc, mc->tw_key, mc->tw_iv); +} + +static void work_done_handler (OTPChecker *mc) +{ + ASSERT(mc->tw_have) + DebugObject_Access(&mc->d_obj); + + // free work + BThreadWork_Free(&mc->tw); + mc->tw_have = 0; + + // update next table number + mc->next_table = bmodadd_int(mc->next_table, 1, mc->num_tables); + + // update number of used tables if not all are used yet + if (mc->tables_used < mc->num_tables) { + mc->tables_used++; + } + + // call handler + if (mc->handler) { + mc->handler(mc->user); + return; + } +} + +int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd) +{ + ASSERT(num_otps > 0) + ASSERT(BEncryption_cipher_valid(cipher)) + ASSERT(num_tables > 0) + + // init arguments + mc->num_otps = num_otps; + mc->cipher = cipher; + mc->num_tables = num_tables; + mc->twd = twd; + + // set no handlers + mc->handler = NULL; + + // set number of entries + if (mc->num_otps > INT_MAX / 2) { + goto fail0; + } + mc->num_entries = 2 * mc->num_otps; + + // set no tables used + mc->tables_used = 0; + mc->next_table = 0; + + // initialize calculator + if (!OTPCalculator_Init(&mc->calc, mc->num_otps, cipher)) { + goto fail0; + } + + // allocate tables + if (!(mc->tables = (struct OTPChecker_table *)BAllocArray(mc->num_tables, sizeof(mc->tables[0])))) { + goto fail1; + } + + // allocate entries + if (!(mc->entries = (struct OTPChecker_entry *)BAllocArray2(mc->num_tables, mc->num_entries, sizeof(mc->entries[0])))) { + goto fail2; + } + + // initialize tables + for (int i = 0; i < mc->num_tables; i++) { + struct OTPChecker_table *table = &mc->tables[i]; + table->entries = mc->entries + (size_t)i * mc->num_entries; + OTPChecker_Table_Empty(mc, table); + } + + // have no work + mc->tw_have = 0; + + DebugObject_Init(&mc->d_obj); + return 1; + +fail2: + BFree(mc->tables); +fail1: + OTPCalculator_Free(&mc->calc); +fail0: + return 0; +} + +void OTPChecker_Free (OTPChecker *mc) +{ + DebugObject_Free(&mc->d_obj); + + // free work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + } + + // free entries + BFree(mc->entries); + + // free tables + BFree(mc->tables); + + // free calculator + OTPCalculator_Free(&mc->calc); +} + +void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(mc->next_table >= 0) + ASSERT(mc->next_table < mc->num_tables) + DebugObject_Access(&mc->d_obj); + + // free existing work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + } + + // set table's seed ID + mc->tables[mc->next_table].id = seed_id; + + // copy key and IV + memcpy(mc->tw_key, key, BEncryption_cipher_key_size(mc->cipher)); + memcpy(mc->tw_iv, iv, BEncryption_cipher_block_size(mc->cipher)); + + // start work + BThreadWork_Init(&mc->tw, mc->twd, (BThreadWork_handler_done)work_done_handler, mc, (BThreadWork_work_func)work_func, mc); + + // set have work + mc->tw_have = 1; +} + +void OTPChecker_RemoveSeeds (OTPChecker *mc) +{ + DebugObject_Access(&mc->d_obj); + + // free existing work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + mc->tw_have = 0; + } + + mc->tables_used = 0; + mc->next_table = 0; +} + +int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp) +{ + DebugObject_Access(&mc->d_obj); + + // try tables in reverse order + for (int i = 1; i <= mc->tables_used; i++) { + int table_index = bmodadd_int(mc->next_table, mc->num_tables - i, mc->num_tables); + if (table_index == mc->next_table && mc->tw_have) { + // ignore table that is being generated + continue; + } + + struct OTPChecker_table *table = &mc->tables[table_index]; + if (table->id == seed_id) { + return OTPChecker_Table_CheckOTP(mc, table, otp); + } + } + + return 0; +} + +void OTPChecker_SetHandlers (OTPChecker *mc, OTPChecker_handler handler, void *user) +{ + DebugObject_Access(&mc->d_obj); + + mc->handler = handler; + mc->user = user; +} diff --git a/app/src/main/jni/badvpn/security/OTPChecker.h b/app/src/main/jni/badvpn/security/OTPChecker.h new file mode 100644 index 0000000..d81a91c --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPChecker.h @@ -0,0 +1,148 @@ +/** + * @file OTPChecker.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that checks OTPs agains known seeds. + */ + +#ifndef BADVPN_SECURITY_OTPCHECKER_H +#define BADVPN_SECURITY_OTPCHECKER_H + +#include + +#include +#include +#include +#include +#include +#include + +struct OTPChecker_entry { + otp_t otp; + int avail; +}; + +struct OTPChecker_table { + uint16_t id; + struct OTPChecker_entry *entries; +}; + +/** + * Handler called when OTP generation for a seed is finished and new OTPs + * can be recognized. + * + * @param user as in {@link OTPChecker_Init} + */ +typedef void (*OTPChecker_handler) (void *user); + +/** + * Object that checks OTPs agains known seeds. + */ +typedef struct { + BThreadWorkDispatcher *twd; + OTPChecker_handler handler; + void *user; + int num_otps; + int cipher; + int num_entries; + int num_tables; + int tables_used; + int next_table; + OTPCalculator calc; + struct OTPChecker_table *tables; + struct OTPChecker_entry *entries; + int tw_have; + BThreadWork tw; + uint8_t tw_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t tw_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + DebugObject d_obj; +} OTPChecker; + +/** + * Initializes the checker. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param mc the object + * @param num_otps number of OTPs to generate from a seed. Must be >0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @param num_tables number of tables to keep, each for one seed. Must be >0. + * @param twd thread work dispatcher + * @return 1 on success, 0 on failure + */ +int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd) WARN_UNUSED; + +/** + * Frees the checker. + * + * @param mc the object + */ +void OTPChecker_Free (OTPChecker *mc); + +/** + * Starts generating OTPs to recognize for a seed. + * OTPs for this seed will not be recognized until the {@link OTPChecker_handler} handler is called. + * If OTPs are still being generated for a previous seed, it will be forgotten. + * + * @param mc the object + * @param seed_id seed identifier + * @param key encryption key + * @param iv initialization vector + */ +void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all active seeds. + * + * @param mc the object + */ +void OTPChecker_RemoveSeeds (OTPChecker *mc); + +/** + * Checks an OTP. + * + * @param mc the object + * @param seed_id identifer of seed whom the OTP is claimed to belong to + * @param otp OTP to check + * @return 1 if the OTP is valid, 0 if not + */ +int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp); + +/** + * Sets handlers. + * + * @param mc the object + * @param handler handler to call when generation of new OTPs is complete, + * after {@link OTPChecker_AddSeed} was called. + * @param user argument to handler + */ +void OTPChecker_SetHandlers (OTPChecker *mc, OTPChecker_handler handler, void *user); + +#endif diff --git a/app/src/main/jni/badvpn/security/OTPGenerator.c b/app/src/main/jni/badvpn/security/OTPGenerator.c new file mode 100644 index 0000000..58a59f5 --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPGenerator.c @@ -0,0 +1,159 @@ +/** + * @file OTPGenerator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void work_func (OTPGenerator *g) +{ + g->otps[!g->cur_calc] = OTPCalculator_Generate(&g->calc[!g->cur_calc], g->tw_key, g->tw_iv, 1); +} + +static void work_done_handler (OTPGenerator *g) +{ + ASSERT(g->tw_have) + DebugObject_Access(&g->d_obj); + + // free work + BThreadWork_Free(&g->tw); + g->tw_have = 0; + + // use new OTPs + g->cur_calc = !g->cur_calc; + g->position = 0; + + // call handler + g->handler(g->user); + return; +} + +int OTPGenerator_Init (OTPGenerator *g, int num_otps, int cipher, BThreadWorkDispatcher *twd, OTPGenerator_handler handler, void *user) +{ + ASSERT(num_otps >= 0) + ASSERT(BEncryption_cipher_valid(cipher)) + + // init arguments + g->num_otps = num_otps; + g->cipher = cipher; + g->twd = twd; + g->handler = handler; + g->user = user; + + // init position + g->position = g->num_otps; + + // init calculator + if (!OTPCalculator_Init(&g->calc[0], g->num_otps, g->cipher)) { + goto fail0; + } + + // init calculator + if (!OTPCalculator_Init(&g->calc[1], g->num_otps, g->cipher)) { + goto fail1; + } + + // set current calculator + g->cur_calc = 0; + + // have no work + g->tw_have = 0; + + DebugObject_Init(&g->d_obj); + return 1; + +fail1: + OTPCalculator_Free(&g->calc[0]); +fail0: + return 0; +} + +void OTPGenerator_Free (OTPGenerator *g) +{ + DebugObject_Free(&g->d_obj); + + // free work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + } + + // free calculator + OTPCalculator_Free(&g->calc[1]); + + // free calculator + OTPCalculator_Free(&g->calc[0]); +} + +void OTPGenerator_SetSeed (OTPGenerator *g, uint8_t *key, uint8_t *iv) +{ + DebugObject_Access(&g->d_obj); + + // free existing work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + } + + // copy key and IV + memcpy(g->tw_key, key, BEncryption_cipher_key_size(g->cipher)); + memcpy(g->tw_iv, iv, BEncryption_cipher_block_size(g->cipher)); + + // start work + BThreadWork_Init(&g->tw, g->twd, (BThreadWork_handler_done)work_done_handler, g, (BThreadWork_work_func)work_func, g); + + // set have work + g->tw_have = 1; +} + +int OTPGenerator_GetPosition (OTPGenerator *g) +{ + DebugObject_Access(&g->d_obj); + + return g->position; +} + +void OTPGenerator_Reset (OTPGenerator *g) +{ + DebugObject_Access(&g->d_obj); + + // free existing work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + g->tw_have = 0; + } + + g->position = g->num_otps; +} + +otp_t OTPGenerator_GetOTP (OTPGenerator *g) +{ + ASSERT(g->position < g->num_otps) + DebugObject_Access(&g->d_obj); + + return g->otps[g->cur_calc][g->position++]; +} diff --git a/app/src/main/jni/badvpn/security/OTPGenerator.h b/app/src/main/jni/badvpn/security/OTPGenerator.h new file mode 100644 index 0000000..f2c83dd --- /dev/null +++ b/app/src/main/jni/badvpn/security/OTPGenerator.h @@ -0,0 +1,134 @@ +/** + * @file OTPGenerator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which generates OTPs for use in sending packets. + */ + +#ifndef BADVPN_SECURITY_OTPGENERATOR_H +#define BADVPN_SECURITY_OTPGENERATOR_H + +#include +#include +#include +#include + +/** + * Handler called when OTP generation for a seed is finished. + * The OTP position is reset to zero before the handler is called. + * + * @param user as in {@link OTPGenerator_Init} + */ +typedef void (*OTPGenerator_handler) (void *user); + +/** + * Object which generates OTPs for use in sending packets. + */ +typedef struct { + int num_otps; + int cipher; + BThreadWorkDispatcher *twd; + OTPGenerator_handler handler; + void *user; + int position; + int cur_calc; + OTPCalculator calc[2]; + otp_t *otps[2]; + int tw_have; + BThreadWork tw; + uint8_t tw_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t tw_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + DebugObject d_obj; +} OTPGenerator; + +/** + * Initializes the generator. + * The object is initialized with number of used OTPs = num_otps. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param g the object + * @param num_otps number of OTPs to generate from a seed. Must be >=0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @param twd thread work dispatcher + * @param handler handler to call when generation of new OTPs is complete, + * after {@link OTPGenerator_SetSeed} was called. + * @param user argument to handler + * @return 1 on success, 0 on failure + */ +int OTPGenerator_Init (OTPGenerator *g, int num_otps, int cipher, BThreadWorkDispatcher *twd, OTPGenerator_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the generator. + * + * @param g the object + */ +void OTPGenerator_Free (OTPGenerator *g); + +/** + * Starts generating OTPs for a seed. + * When generation is complete and the new OTPs may be used, the {@link OTPGenerator_handler} + * handler will be called. + * If OTPs are still being generated for a previous seed, it will be forgotten. + * This call by itself does not affect the OTP position; rather the position is set to zero + * before the handler is called. + * + * @param g the object + * @param key encryption key + * @param iv initialization vector + */ +void OTPGenerator_SetSeed (OTPGenerator *g, uint8_t *key, uint8_t *iv); + +/** + * Returns the number of OTPs used up from the current seed so far. + * If there is no seed yet, returns num_otps. + * + * @param g the object + * @return number of used OTPs + */ +int OTPGenerator_GetPosition (OTPGenerator *g); + +/** + * Sets the number of used OTPs to num_otps. + * + * @param g the object + */ +void OTPGenerator_Reset (OTPGenerator *g); + +/** + * Generates a single OTP. + * The number of used OTPs must be < num_otps. + * The number of used OTPs is incremented. + * + * @param g the object + */ +otp_t OTPGenerator_GetOTP (OTPGenerator *g); + +#endif diff --git a/app/src/main/jni/badvpn/server/CMakeLists.txt b/app/src/main/jni/badvpn/server/CMakeLists.txt new file mode 100644 index 0000000..1d02432 --- /dev/null +++ b/app/src/main/jni/badvpn/server/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(badvpn-server server.c) +target_link_libraries(badvpn-server system flow flowextra nspr_support predicate security ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-server + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-server.8 + DESTINATION share/man/man8 +) diff --git a/app/src/main/jni/badvpn/server/badvpn-server.8 b/app/src/main/jni/badvpn/server/badvpn-server.8 new file mode 100644 index 0000000..b8c60e5 --- /dev/null +++ b/app/src/main/jni/badvpn/server/badvpn-server.8 @@ -0,0 +1,190 @@ +.TH badvpn-server 8 "21 June 2011" +.SH NAME +badvpn-server \- chat server for the BadVPN peer-to-peer VPN system +.SH SYNOPSIS +.B badvpn-server +.RS +.RB "[" --help "]" +.br +.RB "[" --version "]" +.br +.RB "[" --logger " ]" +.br +(logger=syslog? +.br +.RS +.br +.RB "[" --syslog-facility " ]" +.br +.RB "[" --syslog-ident " ]" +.br +.RE +) +.br +.RB "[" --loglevel " <0-5/none/error/warning/notice/info/debug>]" +.br +.RB "[" --channel-loglevel " <0-5/none/error/warning/notice/info/debug>] ..." +.br +.RB "[" --listen-addr " ] ..." +.br +.RB "[" --ssl " " --nssdb " " --server-cert-name " ]" +.br +.RB "[" --comm-predicate " ]" +.br +.RB "[" --relay-predicate " ]" +.br +.RB "[" --client-socket-sndbuf " ]" +.br +.RE +.SH INTRODUCTION +.P +This page documents the BadVPN server, which is used in a BadVPN VPN network by peers to +talk to each other in order to establish data connections. For a general description of +BadVPN, see +.BR badvpn (7). +.SH DESCRIPTION +.P +The BadVPN server is a chat server used by nodes in the VPN network to talk to each other +in order to establish data connections. Once it initializes, the server only terminates +if a signal is received. +.SH OPTIONS +.P +The BadVPN server is configured entirely from command line. +.TP +.BR --help +Print version and command line syntax and exit. +.TP +.BR --version +Print version and exit. +.TP +.BR --logger " " +Select where to log messages. Default is stdout. Syslog is not available on Windows. +.TP +.BR --syslog-facility " " +When logging to syslog, set the logging facility. The facility name must be in lower case. +.TP +.BR --syslog-ident " " +When logging to syslog, set the ident. +.TP +.BR --loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the default logging level. +.TP +.BR --channel-loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the logging level for a specific logging channel. +.TP +.BR --listen-addr " " +Add an address for the server to listen on. See below for address format. +.TP +.BR --ssl +Use TLS. Requires --nssdb and --server-cert-name. +.TP +.BR --nssdb " " +When using TLS, the NSS database to use. Probably something like sql:/some/folder. +.TP +.BR --server-cert-name " " +When using TLS, the name of the certificate to use. The certificate must be readily accessible. +.TP +.BR --comm-predicate " " +Set a predicate to define which pairs of clients are allowed to communicate. The predicate is a +logical expression; see below for details. Available functions: +.br +.BR p1name "(string)" +- true if the TLS common name of peer 1 equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR p1addr "(string)" +- true if the IP address of peer 1 equals the given string. The string must not be a name. +.br +.BR p2name "(string)" +- true if the TLS common name of peer 2 equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR p2addr "(string)" +- true if the IP address of peer 2 equals the given string. The string must not be a name. +.br +There is no rule as to which is peer 1 and which peer 2. When the server needs to determine +whether to allow two peers to communicate, it evaluates the predicate once and in no specific order. +.TP +.BR --relay-predicate " " +Set a predicate to define how peers can relay data through other peers. The predicate is a +logical expression; see below for details. If the predicate evaluates to true, peer P can relay data +through peer R. Available functions: +.br +.BR pname "(string)" +- true if the TLS common name of peer P peer equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR paddr "(string)" +- true if the IP address of peer P equals the given string. The string must not be a name. +.br +.BR rname "(string)" +- true if the TLS common name of peer R peer equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR raddr "(string)" +- true if the IP address of peer R equals the given string. The string must not be a name. +.br +.TP +.BR --client-socket-sndbuf " " +Sets the value of the SO_SNDBUF socket option for client TCP sockets (zero to not set). Lower values +will improve fairness when data from multiple peers is being sent to a given peer, but may result in lower +bandwidth if the network's bandwidth-delay product to too big. +.SH "EXIT CODE" +.P +If initialization fails, exits with code 1. Otherwise runs until termination is requested and exits with code 1. +.SH "ADDRESS FORMAT" +.P +Addresses have the form ipaddr:port, where ipaddr is either an IPv4 address (name or numeric), or an +IPv6 address enclosed in brackets [] (name or numeric again). +.SH PREDICATES +.P +The BadVPN server includes a small predicate language used to define certain policies. +Syntax and semantics of the language are described here. +.TP +.BR true +Logical true constant. Evaluates to 1. +.TP +.BR false +Logical false constant. Evaluates to 0. +.TP +.BR NOT " expression" +Logical negation. If the expression evaluates to error, the +negation evaluates to error. +.TP +.RB "expression " OR " expression" +Logical disjunction. The second expression is only evaluated +if the first expression evaluates to false. If a sub-expression +evaluates to error, the disjunction evaluates to error. +.TP +.RB "expression " AND " expression" +Logical conjunction. The second expression is only evaluated +if the first expression evaluates to true. If a sub-expression +evaluates to error, the conjunction evaluates to error. +.TP +.RB function "(" "arg" "," " ..." "," " arg" ")" +Evaluation of a user-provided function (function is the name of the +function, [a-zA-Z0-9_]+). +If the function with the given name does not exist, it evaluates to +error. +Arguments are evaluated from left to right. Each argument can either +be a logical expression or a string (characters enclosed in double +quotes, without any double quote). +If an argument is encountered, but all needed arguments have already +been evaluated, the function evaluates to error. +If an argument is of wrong type, it is not evaluated and the function +evaluates to error. +If an argument evaluates to error, the function evaluates to error. +If after all arguments have been evaluated, the function needs more +arguments, it evaluates to error. +Then the handler function is called. If it returns anything other +than 1 and 0, the function evaluates to error. Otherwise it evaluates +to what the handler function returned. +.SH "EXAMPLES" +.P +For examples of using BadVPN, see +.BR badvpn (7). +.SH "SEE ALSO" +.BR badvpn-client (8), +.BR badvpn (7) +.SH AUTHORS +Ambroz Bizjak diff --git a/app/src/main/jni/badvpn/server/server.c b/app/src/main/jni/badvpn/server/server.c new file mode 100644 index 0000000..231541f --- /dev/null +++ b/app/src/main/jni/badvpn/server/server.c @@ -0,0 +1,2389 @@ +/** + * @file server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +// NSPR and NSS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// BadVPN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// parsed command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int threads; + int use_threads_for_ssl_handshake; + int use_threads_for_ssl_data; + int ssl; + char *nssdb; + char *server_cert_name; + char *listen_addrs[MAX_LISTEN_ADDRS]; + int num_listen_addrs; + char *comm_predicate; + char *relay_predicate; + int client_socket_sndbuf; + int max_clients; +} options; + +// listen addresses +BAddr listen_addrs[MAX_LISTEN_ADDRS]; +int num_listen_addrs; + +// communication predicate +BPredicate comm_predicate; + +// communication predicate functions +BPredicateFunction comm_predicate_func_p1name; +BPredicateFunction comm_predicate_func_p2name; +BPredicateFunction comm_predicate_func_p1addr; +BPredicateFunction comm_predicate_func_p2addr; + +// variables when evaluating the predicate, adjusted before every evaluation +const char *comm_predicate_p1name; +const char *comm_predicate_p2name; +BIPAddr comm_predicate_p1addr; +BIPAddr comm_predicate_p2addr; + +// relay predicate +BPredicate relay_predicate; + +// gateway predicate functions +BPredicateFunction relay_predicate_func_pname; +BPredicateFunction relay_predicate_func_rname; +BPredicateFunction relay_predicate_func_paddr; +BPredicateFunction relay_predicate_func_raddr; + +// variables when evaluating the comm_predicate, adjusted before every evaluation +const char *relay_predicate_pname; +const char *relay_predicate_rname; +BIPAddr relay_predicate_paddr; +BIPAddr relay_predicate_raddr; + +// i/o system +BReactor ss; + +// thread work dispatcher +BThreadWorkDispatcher twd; + +// server certificate if using SSL +CERTCertificate *server_cert; + +// server private key if using SSL +SECKEYPrivateKey *server_key; + +// model NSPR file descriptor to speed up client initialization +PRFileDesc model_dprfd; +PRFileDesc *model_prfd; + +// listeners +BListener listeners[MAX_LISTEN_ADDRS]; +int num_listeners; + +// number of connected clients +int clients_num; + +// ID assigned to last connected client +peerid_t clients_nextid; + +// clients list +LinkedList1 clients; + +// clients tree (by ID) +BAVL clients_tree; + +// prints help text to standard output +static void print_help (const char *name); + +// prints program name and version to standard output +static void print_version (void); + +// parses the command line +static int parse_arguments (int argc, char *argv[]); + +// processes certain command line options +static int process_arguments (void); + +static int ssl_flags (void); + +// handler for program termination request +static void signal_handler (void *unused); + +// listener handler, accepts new clients +static void listener_handler (BListener *listener); + +// frees resources used by a client +static void client_dealloc (struct client_data *client); + +static int client_compute_buffer_size (struct client_data *client); + +// initializes the I/O porition of the client +static int client_init_io (struct client_data *client); + +// deallocates the I/O portion of the client. Must have no outgoing flows. +static void client_dealloc_io (struct client_data *client); + +// removes a client +static void client_remove (struct client_data *client); + +// job to finish removal after clients are informed +static void client_dying_job (struct client_data *client); + +// appends client log prefix +static void client_logfunc (struct client_data *client); + +// passes a message to the logger, prepending about the client +static void client_log (struct client_data *client, int level, const char *fmt, ...); + +// client activity timer handler. Removes the client. +static void client_disconnect_timer_handler (struct client_data *client); + +// BConnection handler +static void client_connection_handler (struct client_data *client, int event); + +// BSSLConnection handler +static void client_sslcon_handler (struct client_data *client, int event); + +// decoder handler +static void client_decoder_handler_error (struct client_data *client); + +// provides a buffer for sending a control packet to the client +static int client_start_control_packet (struct client_data *client, void **data, int len); + +// submits a packet written after client_start_control_packet +static void client_end_control_packet (struct client_data *client, uint8_t id); + +// sends a newclient message to a client +static int client_send_newclient (struct client_data *client, struct client_data *nc, int relay_server, int relay_client); + +// sends an endclient message to a client +static int client_send_endclient (struct client_data *client, peerid_t end_id); + +// handler for packets received from the client +static void client_input_handler_send (struct client_data *client, uint8_t *data, int data_len); + +// processes hello packets from clients +static void process_packet_hello (struct client_data *client, uint8_t *data, int data_len); + +// processes outmsg packets from clients +static void process_packet_outmsg (struct client_data *client, uint8_t *data, int data_len); + +// processes resetpeer packets from clients +static void process_packet_resetpeer (struct client_data *client, uint8_t *data, int data_len); + +// processes acceptpeer packets from clients +static void process_packet_acceptpeer (struct client_data *client, uint8_t *data, int data_len); + +// creates a peer flow +static struct peer_flow * peer_flow_create (struct client_data *src_client, struct client_data *dest_client); + +// deallocates a peer flow +static void peer_flow_dealloc (struct peer_flow *flow); + +static int peer_flow_init_io (struct peer_flow *flow); +static void peer_flow_free_io (struct peer_flow *flow); + +// disconnects the source client from a peer flow +static void peer_flow_disconnect (struct peer_flow *flow); + +// provides a buffer for sending a peer-to-peer packet +static int peer_flow_start_packet (struct peer_flow *flow, void **data, int len); + +// submits a peer-to-peer packet written after peer_flow_start_packet +static void peer_flow_end_packet (struct peer_flow *flow, uint8_t type); + +// handler called by the queue when a peer flow can be freed after its source has gone away +static void peer_flow_handler_canremove (struct peer_flow *flow); + +static void peer_flow_start_reset (struct peer_flow *flow); +static void peer_flow_drive_reset (struct peer_flow *flow); + +static void peer_flow_reset_qflow_handler_busy (struct peer_flow *flow); + +// resets clients knowledge after the timer expires +static void peer_flow_reset_timer_handler (struct peer_flow *flow); + +// generates a client ID to be used for a newly connected client +static peerid_t new_client_id (void); + +// finds a client by its ID +static struct client_data * find_client_by_id (peerid_t id); + +// checks if two clients are allowed to communicate. May depend on the order +// of the clients. +static int clients_allowed (struct client_data *client1, struct client_data *client2); + +// communication predicate function p1name +static int comm_predicate_func_p1name_cb (void *user, void **args); + +// communication predicate function p2name +static int comm_predicate_func_p2name_cb (void *user, void **args); + +// communication predicate function p1addr +static int comm_predicate_func_p1addr_cb (void *user, void **args); + +// communication predicate function p2addr +static int comm_predicate_func_p2addr_cb (void *user, void **args); + +// checks if relay is allowed for a client through another client +static int relay_allowed (struct client_data *client, struct client_data *relay); + +// relay predicate function pname +static int relay_predicate_func_pname_cb (void *user, void **args); + +// relay predicate function rname +static int relay_predicate_func_rname_cb (void *user, void **args); + +// relay predicate function paddr +static int relay_predicate_func_paddr_cb (void *user, void **args); + +// relay predicate function raddr +static int relay_predicate_func_raddr_cb (void *user, void **args); + +// comparator for peerid_t used in AVL tree +static int peerid_comparator (void *unused, peerid_t *p1, peerid_t *p2); + +static struct peer_know * create_know (struct client_data *from, struct client_data *to, int relay_server, int relay_client); +static void remove_know (struct peer_know *k); +static void know_inform_job_handler (struct peer_know *k); +static void uninform_know (struct peer_know *k); +static void know_uninform_job_handler (struct peer_know *k); + +static int launch_pair (struct peer_flow *flow_to); + +// find flow from a client to some client +static struct peer_flow * find_flow (struct client_data *client, peerid_t dest_id); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + if (options.ssl) { + // initialize NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // initialize i/o layer types + if (!DummyPRFileDesc_GlobalInit()) { + BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed"); + goto fail01; + } + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail01; + } + + // initialize NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail01; + } + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // initialize server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.server_cert_name, &server_cert, &server_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail03; + } + + // initialize model SSL fd + DummyPRFileDesc_Create(&model_dprfd); + if (!(model_prfd = SSL_ImportFD(NULL, &model_dprfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&model_dprfd) == PR_SUCCESS) + goto fail04; + } + + // set server certificate + if (SSL_ConfigSecureServer(model_prfd, server_cert, server_key, NSS_FindCertKEAType(server_cert)) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail05; + } + } + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init communication predicate + if (options.comm_predicate) { + // init predicate + if (!BPredicate_Init(&comm_predicate, options.comm_predicate)) { + BLog(BLOG_ERROR, "BPredicate_Init failed"); + goto fail1; + } + + // init functions + int args[] = {PREDICATE_TYPE_STRING}; + BPredicateFunction_Init(&comm_predicate_func_p1name, &comm_predicate, "p1name", args, 1, comm_predicate_func_p1name_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p2name, &comm_predicate, "p2name", args, 1, comm_predicate_func_p2name_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p1addr, &comm_predicate, "p1addr", args, 1, comm_predicate_func_p1addr_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p2addr, &comm_predicate, "p2addr", args, 1, comm_predicate_func_p2addr_cb, NULL); + } + + // init relay predicate + if (options.relay_predicate) { + // init predicate + if (!BPredicate_Init(&relay_predicate, options.relay_predicate)) { + BLog(BLOG_ERROR, "BPredicate_Init failed"); + goto fail2; + } + + // init functions + int args[] = {PREDICATE_TYPE_STRING}; + BPredicateFunction_Init(&relay_predicate_func_pname, &relay_predicate, "pname", args, 1, relay_predicate_func_pname_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_rname, &relay_predicate, "rname", args, 1, relay_predicate_func_rname_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_paddr, &relay_predicate, "paddr", args, 1, relay_predicate_func_paddr_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_raddr, &relay_predicate, "raddr", args, 1, relay_predicate_func_raddr_cb, NULL); + } + + // init time + BTime_Init(); + + // initialize reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail3; + } + + // init thread work dispatcher + if (!BThreadWorkDispatcher_Init(&twd, &ss, options.threads)) { + BLog(BLOG_ERROR, "BThreadWorkDispatcher_Init failed"); + goto fail3a; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail4; + } + + // initialize number of clients + clients_num = 0; + + // first client ID will be zero + clients_nextid = 0; + + // initialize clients linked list + LinkedList1_Init(&clients); + + // initialize clients tree + BAVL_Init(&clients_tree, OFFSET_DIFF(struct client_data, id, tree_node), (BAVL_comparator)peerid_comparator, NULL); + + // initialize listeners + num_listeners = 0; + while (num_listeners < num_listen_addrs) { + if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "BListener_Init failed"); + goto fail10; + } + num_listeners++; + } + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&clients)) { + struct client_data *client = UPPER_OBJECT(node, struct client_data, list_node); + + // remove outgoing knows + LinkedList1Node *node2; + while (node2 = LinkedList1_GetFirst(&client->know_out_list)) { + struct peer_know *k = UPPER_OBJECT(node2, struct peer_know, from_node); + remove_know(k); + } + + // remove incoming knows + LinkedList1Node *node3; + while (node3 = LinkedList1_GetFirst(&client->know_in_list)) { + struct peer_know *k = UPPER_OBJECT(node3, struct peer_know, to_node); + remove_know(k); + } + + // remove outgoing flows + LinkedList1Node *flow_node; + while (flow_node = LinkedList1_GetFirst(&client->peer_out_flows_list)) { + struct peer_flow *flow = UPPER_OBJECT(flow_node, struct peer_flow, src_list_node); + ASSERT(flow->src_client == client) + + // allow freeing queue flows at dest + PacketPassFairQueue_PrepareFree(&flow->dest_client->output_peers_fairqueue); + + // deallocate flow + peer_flow_dealloc(flow); + } + + // deallocate client + client_dealloc(client); + } +fail10: + while (num_listeners > 0) { + num_listeners--; + BListener_Free(&listeners[num_listeners]); + } + + BSignal_Finish(); +fail4: + BThreadWorkDispatcher_Free(&twd); +fail3a: + BReactor_Free(&ss); +fail3: + if (options.relay_predicate) { + BPredicateFunction_Free(&relay_predicate_func_raddr); + BPredicateFunction_Free(&relay_predicate_func_paddr); + BPredicateFunction_Free(&relay_predicate_func_rname); + BPredicateFunction_Free(&relay_predicate_func_pname); + BPredicate_Free(&relay_predicate); + } +fail2: + if (options.comm_predicate) { + BPredicateFunction_Free(&comm_predicate_func_p2addr); + BPredicateFunction_Free(&comm_predicate_func_p1addr); + BPredicateFunction_Free(&comm_predicate_func_p2name); + BPredicateFunction_Free(&comm_predicate_func_p1name); + BPredicate_Free(&comm_predicate); + } +fail1: + if (options.ssl) { +fail05: + ASSERT_FORCE(PR_Close(model_prfd) == PR_SUCCESS) +fail04: + CERT_DestroyCertificate(server_cert); + SECKEY_DestroyPrivateKey(server_key); +fail03: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail02: + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail01: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--threads ]\n" + " [--use-threads-for-ssl-handshake]\n" + " [--use-threads-for-ssl-data]\n" + " [--listen-addr ] ...\n" + " [--ssl --nssdb --server-cert-name ]\n" + " [--comm-predicate ]\n" + " [--relay-predicate ]\n" + " [--client-socket-sndbuf ]\n" + " [--max-clients ]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.threads = 0; + options.use_threads_for_ssl_handshake = 0; + options.use_threads_for_ssl_data = 0; + options.ssl = 0; + options.nssdb = NULL; + options.server_cert_name = NULL; + options.num_listen_addrs = 0; + options.comm_predicate = NULL; + options.relay_predicate = NULL; + options.client_socket_sndbuf = CLIENT_DEFAULT_SOCKET_SNDBUF; + options.max_clients = DEFAULT_MAX_CLIENTS; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--threads")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.threads = atoi(argv[i + 1]); + i++; + } + else if (!strcmp(arg, "--use-threads-for-ssl-handshake")) { + options.use_threads_for_ssl_handshake = 1; + } + else if (!strcmp(arg, "--use-threads-for-ssl-data")) { + options.use_threads_for_ssl_data = 1; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_listen_addrs == MAX_LISTEN_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.listen_addrs[options.num_listen_addrs] = argv[i + 1]; + options.num_listen_addrs++; + i++; + } + else if (!strcmp(arg, "--comm-predicate")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.comm_predicate = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--relay-predicate")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.relay_predicate = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.client_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else { + fprintf(stderr, "%s: unknown option\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!!options.nssdb != options.ssl) { + fprintf(stderr, "--ssl and --nssdb must be used together\n"); + return 0; + } + + if (!!options.server_cert_name != options.ssl) { + fprintf(stderr, "--ssl and --server-cert-name must be used together\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen addresses + num_listen_addrs = 0; + while (num_listen_addrs < options.num_listen_addrs) { + if (!BAddr_Parse(&listen_addrs[num_listen_addrs], options.listen_addrs[num_listen_addrs], NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + num_listen_addrs++; + } + + return 1; +} + +int ssl_flags (void) +{ + int flags = 0; + if (options.use_threads_for_ssl_handshake) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE; + } + if (options.use_threads_for_ssl_data) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_IO; + } + return flags; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void listener_handler (BListener *listener) +{ + if (clients_num == options.max_clients) { + BLog(BLOG_WARNING, "too many clients for new client"); + goto fail0; + } + + // allocate the client structure + struct client_data *client = (struct client_data *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "failed to allocate client"); + goto fail0; + } + + // accept connection + if (!BConnection_Init(&client->con, BConnection_source_listener(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // limit socket send buffer, else our scheduling is pointless + if (options.client_socket_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&client->con, options.client_socket_sndbuf)) { + BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + // assign ID + client->id = new_client_id(); + + // set no common name + client->common_name = NULL; + + // now client_log() works + + // init connection interfaces + BConnection_SendAsync_Init(&client->con); + BConnection_RecvAsync_Init(&client->con); + + if (options.ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&client->bottom_prfd, BConnection_SendAsync_GetIf(&client->con), BConnection_RecvAsync_GetIf(&client->con), &twd, ssl_flags())) { + client_log(client, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(client->ssl_prfd = SSL_ImportFD(model_prfd, &client->bottom_prfd))) { + client_log(client, BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&client->bottom_prfd) == PR_SUCCESS) + goto fail2; + } + + // set server mode + if (SSL_ResetHandshake(client->ssl_prfd, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(client->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(client->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + + // init SSL connection + BSSLConnection_Init(&client->sslcon, client->ssl_prfd, 1, BReactor_PendingGroup(&ss), client, (BSSLConnection_handler)client_sslcon_handler); + } else { + // initialize I/O + if (!client_init_io(client)) { + goto fail2; + } + } + + // start disconnect timer + BTimer_Init(&client->disconnect_timer, CLIENT_NO_DATA_TIME_LIMIT, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // link in + clients_num++; + LinkedList1_Append(&clients, &client->list_node); + ASSERT_EXECUTE(BAVL_Insert(&clients_tree, &client->tree_node, NULL)) + + // init knowledge lists + LinkedList1_Init(&client->know_out_list); + LinkedList1_Init(&client->know_in_list); + + // initialize peer flows from us list and tree (flows for sending messages to other clients) + LinkedList1_Init(&client->peer_out_flows_list); + BAVL_Init(&client->peer_out_flows_tree, OFFSET_DIFF(struct peer_flow, dest_client_id, src_tree_node), (BAVL_comparator)peerid_comparator, NULL); + + // init dying + client->dying = 0; + BPending_Init(&client->dying_job, BReactor_PendingGroup(&ss), (BPending_handler)client_dying_job, client); + + // set state + client->initstatus = (options.ssl ? INITSTATUS_HANDSHAKE : INITSTATUS_WAITHELLO); + + client_log(client, BLOG_INFO, "initialized"); + + return; + + if (options.ssl) { +fail3: + ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS) + } +fail2: + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_dealloc (struct client_data *client) +{ + ASSERT(LinkedList1_IsEmpty(&client->know_out_list)) + ASSERT(LinkedList1_IsEmpty(&client->know_in_list)) + ASSERT(LinkedList1_IsEmpty(&client->peer_out_flows_list)) + + // free I/O + if (client->initstatus >= INITSTATUS_WAITHELLO && !client->dying) { + client_dealloc_io(client); + } + + // free dying + BPending_Free(&client->dying_job); + + // link out + BAVL_Remove(&clients_tree, &client->tree_node); + LinkedList1_Remove(&clients, &client->list_node); + clients_num--; + + // stop disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free SSL + if (options.ssl) { + BSSLConnection_Free(&client->sslcon); + ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS) + } + + // free common name + if (client->common_name) { + PORT_Free(client->common_name); + } + + // free connection interfaces + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free memory + free(client); +} + +int client_compute_buffer_size (struct client_data *client) +{ + bsize_t s = bsize_add(bsize_fromsize(1), bsize_mul(bsize_fromsize(2), bsize_fromsize(options.max_clients - 1))); + + if (s.is_overflow || s.value > INT_MAX) { + return INT_MAX; + } else { + return s.value; + } +} + +int client_init_io (struct client_data *client) +{ + StreamPassInterface *send_if = (options.ssl ? BSSLConnection_GetSendIf(&client->sslcon) : BConnection_SendAsync_GetIf(&client->con)); + StreamRecvInterface *recv_if = (options.ssl ? BSSLConnection_GetRecvIf(&client->sslcon) : BConnection_RecvAsync_GetIf(&client->con)); + + // init input + + // init interface + PacketPassInterface_Init(&client->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)client_input_handler_send, client, BReactor_PendingGroup(&ss)); + + // init decoder + if (!PacketProtoDecoder_Init(&client->input_decoder, recv_if, &client->input_interface, BReactor_PendingGroup(&ss), client, + (PacketProtoDecoder_handler_error)client_decoder_handler_error + )) { + client_log(client, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init output common + + // init sender + PacketStreamSender_Init(&client->output_sender, send_if, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(&ss)); + + // init queue + PacketPassPriorityQueue_Init(&client->output_priorityqueue, PacketStreamSender_GetInput(&client->output_sender), BReactor_PendingGroup(&ss), 0); + + // init output control flow + + // init queue flow + PacketPassPriorityQueueFlow_Init(&client->output_control_qflow, &client->output_priorityqueue, -1); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init( + &client->output_control_oflow, SC_MAX_ENC, client_compute_buffer_size(client), + PacketPassPriorityQueueFlow_GetInput(&client->output_control_qflow), BReactor_PendingGroup(&ss) + )) { + client_log(client, BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail2; + } + client->output_control_input = PacketProtoFlow_GetInput(&client->output_control_oflow); + client->output_control_packet_len = -1; + + // init output peers flow + + // init queue flow + // use lower priority than control flow (higher number) + PacketPassPriorityQueueFlow_Init(&client->output_peers_qflow, &client->output_priorityqueue, 0); + + // init fair queue (for different peers) + if (!PacketPassFairQueue_Init(&client->output_peers_fairqueue, PacketPassPriorityQueueFlow_GetInput(&client->output_peers_qflow), BReactor_PendingGroup(&ss), 0, 1)) { + client_log(client, BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail3; + } + + // init list of flows + LinkedList1_Init(&client->output_peers_flows); + + return 1; + +fail3: + PacketPassPriorityQueueFlow_Free(&client->output_peers_qflow); + PacketProtoFlow_Free(&client->output_control_oflow); +fail2: + PacketPassPriorityQueueFlow_Free(&client->output_control_qflow); + // free output common + PacketPassPriorityQueue_Free(&client->output_priorityqueue); + PacketStreamSender_Free(&client->output_sender); + // free input + PacketProtoDecoder_Free(&client->input_decoder); +fail1: + PacketPassInterface_Free(&client->input_interface); + return 0; +} + +void client_dealloc_io (struct client_data *client) +{ + // allow freeing fair queue flows + PacketPassFairQueue_PrepareFree(&client->output_peers_fairqueue); + + // remove flows to us + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&client->output_peers_flows)) { + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, dest_list_node); + ASSERT(flow->dest_client == client) + peer_flow_dealloc(flow); + } + + // allow freeing priority queue flows + PacketPassPriorityQueue_PrepareFree(&client->output_priorityqueue); + + // free output peers flow + PacketPassFairQueue_Free(&client->output_peers_fairqueue); + PacketPassPriorityQueueFlow_Free(&client->output_peers_qflow); + + // free output control flow + PacketProtoFlow_Free(&client->output_control_oflow); + PacketPassPriorityQueueFlow_Free(&client->output_control_qflow); + + // free output common + PacketPassPriorityQueue_Free(&client->output_priorityqueue); + PacketStreamSender_Free(&client->output_sender); + + // free input + PacketProtoDecoder_Free(&client->input_decoder); + PacketPassInterface_Free(&client->input_interface); +} + +void client_remove (struct client_data *client) +{ + ASSERT(!client->dying) + + client_log(client, BLOG_INFO, "removing"); + + // set dying to prevent sending this client anything + client->dying = 1; + + // free I/O now, removing incoming flows + if (client->initstatus >= INITSTATUS_WAITHELLO) { + client_dealloc_io(client); + } + + // remove outgoing knows + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&client->know_out_list)) { + struct peer_know *k = UPPER_OBJECT(node, struct peer_know, from_node); + remove_know(k); + } + + // remove outgoing flows + while (node = LinkedList1_GetFirst(&client->peer_out_flows_list)) { + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, src_list_node); + ASSERT(flow->src_client == client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + + if (flow->have_io && PacketPassFairQueueFlow_IsBusy(&flow->qflow)) { + client_log(client, BLOG_DEBUG, "removing flow to %d later", (int)flow->dest_client->id); + peer_flow_disconnect(flow); + } else { + client_log(client, BLOG_DEBUG, "removing flow to %d now", (int)flow->dest_client->id); + peer_flow_dealloc(flow); + } + } + + // schedule job to finish removal after clients are informed + BPending_Set(&client->dying_job); + + // inform other clients that 'client' is no more + node = LinkedList1_GetFirst(&client->know_in_list); + while (node) { + LinkedList1Node *next = LinkedList1Node_Next(node); + struct peer_know *k = UPPER_OBJECT(node, struct peer_know, to_node); + uninform_know(k); + node = next; + } +} + +void client_dying_job (struct client_data *client) +{ + ASSERT(client->dying) + ASSERT(LinkedList1_IsEmpty(&client->know_in_list)) + + client_dealloc(client); + return; +} + +void client_logfunc (struct client_data *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client %d (%s)", (int)client->id, addr); + if (client->common_name) { + BLog_Append(" (%s)", client->common_name); + } + BLog_Append(": "); +} + +void client_log (struct client_data *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client_data *client) +{ + ASSERT(!client->dying) + + client_log(client, BLOG_INFO, "timed out"); + + client_remove(client); + return; +} + +void client_connection_handler (struct client_data *client, int event) +{ + ASSERT(!client->dying) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "connection closed"); + } else { + client_log(client, BLOG_INFO, "connection error"); + } + + client_remove(client); + return; +} + +void client_sslcon_handler (struct client_data *client, int event) +{ + ASSERT(options.ssl) + ASSERT(!client->dying) + ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR) + ASSERT(!(event == BSSLCONNECTION_EVENT_UP) || client->initstatus == INITSTATUS_HANDSHAKE) + + if (event == BSSLCONNECTION_EVENT_ERROR) { + client_log(client, BLOG_ERROR, "SSL error"); + client_remove(client); + return; + } + + // get client certificate + CERTCertificate *cert = SSL_PeerCertificate(client->ssl_prfd); + if (!cert) { + client_log(client, BLOG_ERROR, "SSL_PeerCertificate failed"); + goto fail0; + } + + // remember common name + if (!(client->common_name = CERT_GetCommonName(&cert->subject))) { + client_log(client, BLOG_NOTICE, "CERT_GetCommonName failed"); + goto fail1; + } + + // store certificate + SECItem der = cert->derCert; + if (der.len > sizeof(client->cert)) { + client_log(client, BLOG_NOTICE, "client certificate too big"); + goto fail1; + } + memcpy(client->cert, der.data, der.len); + client->cert_len = der.len; + + PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + client_log(client, BLOG_ERROR, "PORT_NewArena failed"); + goto fail1; + } + + // encode certificate + memset(&der, 0, sizeof(der)); + if (!SEC_ASN1EncodeItem(arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate))) { + client_log(client, BLOG_ERROR, "SEC_ASN1EncodeItem failed"); + goto fail2; + } + + // store re-encoded certificate (for compatibility with old clients) + if (der.len > sizeof(client->cert_old)) { + client_log(client, BLOG_NOTICE, "client certificate too big"); + goto fail2; + } + memcpy(client->cert_old, der.data, der.len); + client->cert_old_len = der.len; + + // init I/O chains + if (!client_init_io(client)) { + goto fail2; + } + + PORT_FreeArena(arena, PR_FALSE); + CERT_DestroyCertificate(cert); + + // set client state + client->initstatus = INITSTATUS_WAITHELLO; + + client_log(client, BLOG_INFO, "handshake complete"); + + return; + + // handle errors +fail2: + PORT_FreeArena(arena, PR_FALSE); +fail1: + CERT_DestroyCertificate(cert); +fail0: + client_remove(client); +} + +void client_decoder_handler_error (struct client_data *client) +{ + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + + client_log(client, BLOG_ERROR, "decoder error"); + + client_remove(client); + return; +} + +int client_start_control_packet (struct client_data *client, void **data, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + ASSERT(client->output_control_packet_len == -1) + +#ifdef SIMULATE_OUT_OF_CONTROL_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_OUT_OF_CONTROL_BUFFER) { + client_log(client, BLOG_INFO, "out of control buffer, removing"); + client_remove(client); + return -1; + } +#endif + + // obtain location for writing the packet + if (!BufferWriter_StartPacket(client->output_control_input, &client->output_control_packet)) { + // out of buffer, kill client + client_log(client, BLOG_INFO, "out of control buffer, removing"); + client_remove(client); + return -1; + } + + client->output_control_packet_len = len; + + if (data) { + *data = client->output_control_packet + sizeof(struct sc_header); + } + + return 0; +} + +void client_end_control_packet (struct client_data *client, uint8_t type) +{ + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + ASSERT(client->output_control_packet_len >= 0) + ASSERT(client->output_control_packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = htol8(type); + memcpy(client->output_control_packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(client->output_control_input, sizeof(struct sc_header) + client->output_control_packet_len); + + client->output_control_packet_len = -1; +} + +int client_send_newclient (struct client_data *client, struct client_data *nc, int relay_server, int relay_client) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + ASSERT(nc->initstatus == INITSTATUS_COMPLETE) + ASSERT(!nc->dying) + + int flags = 0; + if (relay_server) { + flags |= SCID_NEWCLIENT_FLAG_RELAY_SERVER; + } + if (relay_client) { + flags |= SCID_NEWCLIENT_FLAG_RELAY_CLIENT; + } + if (options.ssl && client->version > SC_OLDVERSION_NOSSL && nc->version > SC_OLDVERSION_NOSSL) { + flags |= SCID_NEWCLIENT_FLAG_SSL; + } + + uint8_t *cert_data = NULL; + int cert_len = 0; + if (options.ssl) { + cert_data = (client->version == SC_OLDVERSION_BROKENCERT ? nc->cert_old : nc->cert); + cert_len = (client->version == SC_OLDVERSION_BROKENCERT ? nc->cert_old_len : nc->cert_len); + } + + struct sc_server_newclient omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg) + cert_len) < 0) { + return -1; + } + omsg.id = htol16(nc->id); + omsg.flags = htol16(flags); + memcpy(pack, &omsg, sizeof(omsg)); + if (cert_len > 0) { + memcpy((char *)pack + sizeof(omsg), cert_data, cert_len); + } + client_end_control_packet(client, SCID_NEWCLIENT); + + return 0; +} + +int client_send_endclient (struct client_data *client, peerid_t end_id) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + + struct sc_server_endclient omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg)) < 0) { + return -1; + } + omsg.id = htol16(end_id); + memcpy(pack, &omsg, sizeof(omsg)); + client_end_control_packet(client, SCID_ENDCLIENT); + + return 0; +} + +void client_input_handler_send (struct client_data *client, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_ENC) + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + + // accept packet + PacketPassInterface_Done(&client->input_interface); + + // restart disconnect timer + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // parse header + if (data_len < sizeof(struct sc_header)) { + client_log(client, BLOG_NOTICE, "packet too short"); + client_remove(client); + return; + } + struct sc_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t type = ltoh8(header.type); + + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_PAYLOAD) + + // perform action based on packet type + switch (type) { + case SCID_KEEPALIVE: + client_log(client, BLOG_DEBUG, "received keep-alive"); + return; + case SCID_CLIENTHELLO: + process_packet_hello(client, data, data_len); + return; + case SCID_OUTMSG: + process_packet_outmsg(client, data, data_len); + return; + case SCID_RESETPEER: + process_packet_resetpeer(client, data, data_len); + return; + case SCID_ACCEPTPEER: + process_packet_acceptpeer(client, data, data_len); + return; + default: + client_log(client, BLOG_NOTICE, "unknown packet type %d, removing", (int)type); + client_remove(client); + return; + } +} + +void process_packet_hello (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_WAITHELLO) { + client_log(client, BLOG_NOTICE, "hello: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_hello)) { + client_log(client, BLOG_NOTICE, "hello: invalid length"); + client_remove(client); + return; + } + + struct sc_client_hello msg; + memcpy(&msg, data, sizeof(msg)); + client->version = ltoh16(msg.version); + + switch (client->version) { + case SC_VERSION: + case SC_OLDVERSION_NOSSL: + case SC_OLDVERSION_BROKENCERT: + break; + default: + client_log(client, BLOG_ERROR, "hello: unknown version (%d)", client->version); + client_remove(client); + return; + } + + client_log(client, BLOG_INFO, "received hello"); + + // set client state to complete + client->initstatus = INITSTATUS_COMPLETE; + + // publish client + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&clients); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct client_data *client2 = UPPER_OBJECT(list_node, struct client_data, list_node); + if (client2 == client || client2->initstatus != INITSTATUS_COMPLETE || client2->dying || !clients_allowed(client, client2)) { + continue; + } + + // create flow from client to client2 + struct peer_flow *flow_to = peer_flow_create(client, client2); + if (!flow_to) { + client_log(client, BLOG_ERROR, "failed to allocate flow to %d", (int)client2->id); + goto fail; + } + + // create flow from client2 to client + struct peer_flow *flow_from = peer_flow_create(client2, client); + if (!flow_from) { + client_log(client, BLOG_ERROR, "failed to allocate flow from %d", (int)client2->id); + goto fail; + } + + // set opposite flow pointers + flow_to->opposite = flow_from; + flow_from->opposite = flow_to; + + // launch pair + if (!launch_pair(flow_to)) { + return; + } + } + + // send hello + struct sc_server_hello omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg)) < 0) { + return; + } + omsg.flags = htol16(0); + omsg.id = htol16(client->id); + omsg.clientAddr = (client->addr.type == BADDR_TYPE_IPV4 ? client->addr.ipv4.ip : hton32(0)); + memcpy(pack, &omsg, sizeof(omsg)); + client_end_control_packet(client, SCID_SERVERHELLO); + + return; + +fail: + client_remove(client); +} + +void process_packet_outmsg (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "outmsg: not expected"); + client_remove(client); + return; + } + + if (data_len < sizeof(struct sc_client_outmsg)) { + client_log(client, BLOG_NOTICE, "outmsg: wrong size"); + client_remove(client); + return; + } + + struct sc_client_outmsg msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + int payload_size = data_len - sizeof(struct sc_client_outmsg); + + if (payload_size > SC_MAX_MSGLEN) { + client_log(client, BLOG_NOTICE, "outmsg: too large payload"); + client_remove(client); + return; + } + + uint8_t *payload = data + sizeof(struct sc_client_outmsg); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + client_log(client, BLOG_INFO, "no flow for message to %d", (int)id); + return; + } + + // if pair is resetting, ignore message + if (flow->resetting || flow->opposite->resetting) { + client_log(client, BLOG_INFO, "pair is resetting; not forwarding message to %d", (int)id); + return; + } + + // if sending client hasn't accepted yet, ignore message + if (!flow->accepted) { + client_log(client, BLOG_INFO, "client hasn't accepted; not forwarding message to %d", (int)id); + return; + } + +#ifdef SIMULATE_OUT_OF_FLOW_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_OUT_OF_FLOW_BUFFER) { + client_log(client, BLOG_WARNING, "simulating error; resetting to %d", (int)flow->dest_client->id); + peer_flow_start_reset(flow); + return; + } +#endif + + // send packet + struct sc_server_inmsg omsg; + void *pack; + if (!peer_flow_start_packet(flow, &pack, sizeof(omsg) + payload_size)) { + // out of buffer, reset these two clients + client_log(client, BLOG_WARNING, "out of buffer; resetting to %d", (int)flow->dest_client->id); + peer_flow_start_reset(flow); + return; + } + omsg.clientid = htol16(client->id); + memcpy(pack, &omsg, sizeof(omsg)); + memcpy((char *)pack + sizeof(omsg), payload, payload_size); + peer_flow_end_packet(flow, SCID_INMSG); +} + +void process_packet_resetpeer (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "resetpeer: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_resetpeer)) { + client_log(client, BLOG_NOTICE, "resetpeer: wrong size"); + client_remove(client); + return; + } + + struct sc_client_resetpeer msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + client_log(client, BLOG_INFO, "no flow for reset to %d", (int)id); + return; + } + + // if pair is resetting, ignore message + if (flow->resetting || flow->opposite->resetting) { + client_log(client, BLOG_INFO, "pair is resetting; not resetting to %d", (int)id); + return; + } + + // if sending client hasn't accepted yet, ignore message + if (!flow->accepted) { + client_log(client, BLOG_INFO, "client hasn't accepted; not resetting to %d", (int)id); + return; + } + + client_log(client, BLOG_WARNING, "resetting to %d", (int)flow->dest_client->id); + + // reset clients + peer_flow_start_reset(flow); +} + +void process_packet_acceptpeer (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "acceptpeer: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_acceptpeer)) { + client_log(client, BLOG_NOTICE, "acceptpeer: wrong size"); + client_remove(client); + return; + } + + struct sc_client_acceptpeer msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + // the specified client has probably gone away but the sending client didn't know + // that yet; this is expected + client_log(client, BLOG_INFO, "acceptpeer: no flow to %d", (int)id); + return; + } + + // client can only accept once + if (flow->accepted) { + // the previous accept is probably from an old client with the same ID as this one; + // this is bad, disconnect client + client_log(client, BLOG_ERROR, "acceptpeer: already accepted to %d", (int)id); + client_remove(client); + return; + } + + client_log(client, BLOG_INFO, "accepted %d", (int)id); + + // set accepted + flow->accepted = 1; + + // if pair is resetting, continue + if (flow->resetting) { + peer_flow_drive_reset(flow); + } else if (flow->opposite->resetting) { + peer_flow_drive_reset(flow->opposite); + } +} + +struct peer_flow * peer_flow_create (struct client_data *src_client, struct client_data *dest_client) +{ + ASSERT(src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!src_client->dying) + ASSERT(dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!dest_client->dying) + ASSERT(!find_flow(src_client, dest_client->id)) + + // allocate flow structure + struct peer_flow *flow = (struct peer_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set source and destination + flow->src_client = src_client; + flow->dest_client = dest_client; + flow->dest_client_id = dest_client->id; + + // add to source list and tree + LinkedList1_Append(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + ASSERT_EXECUTE(BAVL_Insert(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node, NULL)) + + // add to destination client list + LinkedList1_Append(&flow->dest_client->output_peers_flows, &flow->dest_list_node); + + // have no I/O + flow->have_io = 0; + + // init reset timer + BTimer_Init(&flow->reset_timer, CLIENT_RESET_TIME, (BTimer_handler)peer_flow_reset_timer_handler, flow); + + return flow; + +fail0: + return NULL; +} + +void peer_flow_dealloc (struct peer_flow *flow) +{ + if (flow->have_io) { PacketPassFairQueueFlow_AssertFree(&flow->qflow); } + + // free reset timer + BReactor_RemoveTimer(&ss, &flow->reset_timer); + + // free I/O + if (flow->have_io) { + peer_flow_free_io(flow); + } + + // remove from destination client list + LinkedList1_Remove(&flow->dest_client->output_peers_flows, &flow->dest_list_node); + + // remove from source list and hash table + if (flow->src_client) { + BAVL_Remove(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node); + LinkedList1_Remove(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + } + + // free memory + free(flow); +} + +int peer_flow_init_io (struct peer_flow *flow) +{ + ASSERT(!flow->have_io) + + // init queue flow + PacketPassFairQueueFlow_Init(&flow->qflow, &flow->dest_client->output_peers_fairqueue); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init( + &flow->oflow, SC_MAX_ENC, CLIENT_PEER_FLOW_BUFFER_MIN_PACKETS, + PacketPassFairQueueFlow_GetInput(&flow->qflow), BReactor_PendingGroup(&ss) + )) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + flow->input = PacketProtoFlow_GetInput(&flow->oflow); + + // set no packet + flow->packet_len = -1; + + // set have I/O + flow->have_io = 1; + + return 1; + +fail1: + PacketPassFairQueueFlow_Free(&flow->qflow); + return 0; +} + +void peer_flow_free_io (struct peer_flow *flow) +{ + ASSERT(flow->have_io) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + // free PacketProtoFlow + PacketProtoFlow_Free(&flow->oflow); + + // free queue flow + PacketPassFairQueueFlow_Free(&flow->qflow); + + // set have no I/O + flow->have_io = 0; +} + +void peer_flow_disconnect (struct peer_flow *flow) +{ + ASSERT(flow->src_client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->have_io) + ASSERT(PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + + // stop reset timer + BReactor_RemoveTimer(&ss, &flow->reset_timer); + + // remove from source list and hash table + BAVL_Remove(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node); + LinkedList1_Remove(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + + // set no source + flow->src_client = NULL; + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)peer_flow_handler_canremove, flow); +} + +int peer_flow_start_packet (struct peer_flow *flow, void **data, int len) +{ + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(!flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(flow->packet_len == -1) + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + + // obtain location for writing the packet + if (!BufferWriter_StartPacket(flow->input, &flow->packet)) { + return 0; + } + + // remember packet length + flow->packet_len = len; + + if (data) { + *data = flow->packet + sizeof(struct sc_header); + } + return 1; +} + +void peer_flow_end_packet (struct peer_flow *flow, uint8_t type) +{ + ASSERT(flow->have_io) + ASSERT(flow->packet_len >= 0) + ASSERT(flow->packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = type; + memcpy(flow->packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(flow->input, sizeof(struct sc_header) + flow->packet_len); + + // set have no packet + flow->packet_len = -1; +} + +void peer_flow_handler_canremove (struct peer_flow *flow) +{ + ASSERT(!flow->src_client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->have_io) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + client_log(flow->dest_client, BLOG_DEBUG, "removing old flow"); + + peer_flow_dealloc(flow); + return; +} + +void peer_flow_start_reset (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(!flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(flow->opposite->have_io) + + client_log(flow->src_client, BLOG_INFO, "starting reset to %d", (int)flow->dest_client->id); + + // set resetting + flow->resetting = 1; + + peer_flow_drive_reset(flow); +} + +void peer_flow_drive_reset (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(!BTimer_IsRunning(&flow->reset_timer)) + + // try to free I/O + if (flow->have_io) { + if (PacketPassFairQueueFlow_IsBusy(&flow->qflow)) { + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)peer_flow_reset_qflow_handler_busy, flow); + } else { + peer_flow_free_io(flow); + } + } + + // try to free opposite I/O + if (flow->opposite->have_io) { + if (PacketPassFairQueueFlow_IsBusy(&flow->opposite->qflow)) { + PacketPassFairQueueFlow_SetBusyHandler(&flow->opposite->qflow, (PacketPassFairQueue_handler_busy)peer_flow_reset_qflow_handler_busy, flow->opposite); + } else { + peer_flow_free_io(flow->opposite); + } + } + + // if we still got some I/O, or some client hasn't accepted yet, wait + if (flow->have_io || flow->opposite->have_io || !flow->accepted || !flow->opposite->accepted) { + return; + } + + // set reset timer + BReactor_SetTimer(&ss, &flow->reset_timer); +} + +void peer_flow_reset_qflow_handler_busy (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting || flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(!PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + + if (flow->resetting) { + peer_flow_drive_reset(flow); + } else { + peer_flow_drive_reset(flow->opposite); + } +} + +void peer_flow_reset_timer_handler (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(!flow->have_io) + ASSERT(!flow->opposite->have_io) + ASSERT(flow->accepted) + ASSERT(flow->opposite->accepted) + + client_log(flow->src_client, BLOG_INFO, "finally resetting to %d", (int)flow->dest_client->id); + + struct peer_know *know = flow->know; + struct peer_know *know_opposite = flow->opposite->know; + + // launch pair + if (!launch_pair(flow)) { + return; + } + + // remove old knows + uninform_know(know); + uninform_know(know_opposite); +} + +peerid_t new_client_id (void) +{ + ASSERT(clients_num < options.max_clients) + + for (int i = 0; i < options.max_clients; i++) { + peerid_t id = clients_nextid++; + if (!find_client_by_id(id)) { + return id; + } + } + + ASSERT(0) + return 42; +} + +struct client_data * find_client_by_id (peerid_t id) +{ + BAVLNode *node; + if (!(node = BAVL_LookupExact(&clients_tree, &id))) { + return NULL; + } + + return UPPER_OBJECT(node, struct client_data, tree_node); +} + +int clients_allowed (struct client_data *client1, struct client_data *client2) +{ + ASSERT(client1->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client1->dying) + ASSERT(client2->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client2->dying) + + if (!options.comm_predicate) { + return 1; + } + + // set values to compare against + comm_predicate_p1name = (client1->common_name ? client1->common_name : ""); + comm_predicate_p2name = (client2->common_name ? client2->common_name : ""); + BAddr_GetIPAddr(&client1->addr, &comm_predicate_p1addr); + BAddr_GetIPAddr(&client2->addr, &comm_predicate_p2addr); + + // evaluate predicate + int res = BPredicate_Eval(&comm_predicate); + if (res < 0) { + return 0; + } + + return res; +} + +int comm_predicate_func_p1name_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, comm_predicate_p1name)); +} + +int comm_predicate_func_p2name_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, comm_predicate_p2name)); +} + +int comm_predicate_func_p1addr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_WARNING, "failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &comm_predicate_p1addr); +} + +int comm_predicate_func_p2addr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_WARNING, "failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &comm_predicate_p2addr); +} + +int relay_allowed (struct client_data *client, struct client_data *relay) +{ + if (!options.relay_predicate) { + return 0; + } + + // set values to compare against + relay_predicate_pname = (client->common_name ? client->common_name : ""); + relay_predicate_rname = (relay->common_name ? relay->common_name : ""); + BAddr_GetIPAddr(&client->addr, &relay_predicate_paddr); + BAddr_GetIPAddr(&relay->addr, &relay_predicate_raddr); + + // evaluate predicate + int res = BPredicate_Eval(&relay_predicate); + if (res < 0) { + return 0; + } + + return res; +} + +int relay_predicate_func_pname_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, relay_predicate_pname)); +} + +int relay_predicate_func_rname_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, relay_predicate_rname)); +} + +int relay_predicate_func_paddr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_ERROR, "paddr: failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &relay_predicate_paddr); +} + +int relay_predicate_func_raddr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_ERROR, "raddr: failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &relay_predicate_raddr); +} + +int peerid_comparator (void *unused, peerid_t *p1, peerid_t *p2) +{ + return B_COMPARE(*p1, *p2); +} + +struct peer_know * create_know (struct client_data *from, struct client_data *to, int relay_server, int relay_client) +{ + ASSERT(from->initstatus == INITSTATUS_COMPLETE) + ASSERT(!from->dying) + ASSERT(to->initstatus == INITSTATUS_COMPLETE) + ASSERT(!to->dying) + + // allocate structure + struct peer_know *k = (struct peer_know *)malloc(sizeof(*k)); + if (!k) { + return NULL; + } + + // init arguments + k->from = from; + k->to = to; + k->relay_server = relay_server; + k->relay_client = relay_client; + + // append to lists + LinkedList1_Append(&from->know_out_list, &k->from_node); + LinkedList1_Append(&to->know_in_list, &k->to_node); + + // init and set inform job to inform client 'from' about client 'to' + BPending_Init(&k->inform_job, BReactor_PendingGroup(&ss), (BPending_handler)know_inform_job_handler, k); + BPending_Set(&k->inform_job); + + // init uninform job + BPending_Init(&k->uninform_job, BReactor_PendingGroup(&ss), (BPending_handler)know_uninform_job_handler, k); + + return k; +} + +void remove_know (struct peer_know *k) +{ + // free uninform job + BPending_Free(&k->uninform_job); + + // free inform job + BPending_Free(&k->inform_job); + + // remove from lists + LinkedList1_Remove(&k->to->know_in_list, &k->to_node); + LinkedList1_Remove(&k->from->know_out_list, &k->from_node); + + // free structure + free(k); +} + +void know_inform_job_handler (struct peer_know *k) +{ + ASSERT(!k->from->dying) + ASSERT(!k->to->dying) + + client_send_newclient(k->from, k->to, k->relay_server, k->relay_client); + return; +} + +void uninform_know (struct peer_know *k) +{ + ASSERT(!k->from->dying) + + // if 'from' has not been informed about 'to' yet, remove know, otherwise + // schedule informing 'from' that 'to' is no more + if (BPending_IsSet(&k->inform_job)) { + remove_know(k); + } else { + BPending_Set(&k->uninform_job); + } +} + +void know_uninform_job_handler (struct peer_know *k) +{ + ASSERT(!k->from->dying) + ASSERT(!BPending_IsSet(&k->inform_job)) + + struct client_data *from = k->from; + struct client_data *to = k->to; + + // remove know + remove_know(k); + + // uninform + client_send_endclient(from, to->id); +} + +int launch_pair (struct peer_flow *flow_to) +{ + struct client_data *client = flow_to->src_client; + struct client_data *client2 = flow_to->dest_client; + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + ASSERT(client2->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client2->dying) + ASSERT(!flow_to->have_io) + ASSERT(!flow_to->opposite->have_io) + ASSERT(!BTimer_IsRunning(&flow_to->reset_timer)) + ASSERT(!BTimer_IsRunning(&flow_to->opposite->reset_timer)) + + // init I/O + if (!peer_flow_init_io(flow_to)) { + goto fail; + } + + // init opposite I/O + if (!peer_flow_init_io(flow_to->opposite)) { + goto fail; + } + + // determine relay relations + int relay_to = relay_allowed(client, client2); + int relay_from = relay_allowed(client2, client); + + // create know to + struct peer_know *know_to = create_know(client, client2, relay_to, relay_from); + if (!know_to) { + client_log(client, BLOG_ERROR, "failed to allocate know to %d", (int)client2->id); + goto fail; + } + + // create know from + struct peer_know *know_from = create_know(client2, client, relay_from, relay_to); + if (!know_from) { + client_log(client, BLOG_ERROR, "failed to allocate know from %d", (int)client2->id); + goto fail; + } + + // set know pointers in flows + flow_to->know = know_to; + flow_to->opposite->know = know_from; + + // set not accepted, or assume accepted for old version + flow_to->accepted = (flow_to->src_client->version <= SC_OLDVERSION_NOSSL); + flow_to->opposite->accepted = (flow_to->opposite->src_client->version <= SC_OLDVERSION_NOSSL); + + // set not resetting + flow_to->resetting = 0; + flow_to->opposite->resetting = 0; + + return 1; + +fail: + client_remove(client); + return 0; +} + +struct peer_flow * find_flow (struct client_data *client, peerid_t dest_id) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + + BAVLNode *node = BAVL_LookupExact(&client->peer_out_flows_tree, &dest_id); + if (!node) { + return NULL; + } + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, src_tree_node); + + ASSERT(flow->dest_client->id == dest_id) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + + return flow; +} diff --git a/app/src/main/jni/badvpn/server/server.h b/app/src/main/jni/badvpn/server/server.h new file mode 100644 index 0000000..66103a9 --- /dev/null +++ b/app/src/main/jni/badvpn/server/server.h @@ -0,0 +1,186 @@ +/** + * @file server.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// name of the program +#define PROGRAM_NAME "server" + +// maxiumum number of connected clients. Must be <=2^16. +#define DEFAULT_MAX_CLIENTS 30 +// client output control flow buffer size in packets +// it must hold: initdata, newclient's, endclient's (if other peers die when informing them) +// make it big enough to hold the initial packet burst (initdata, newclient's), +#define CLIENT_CONTROL_BUFFER_MIN_PACKETS (1 + 2*(MAX_CLIENTS - 1)) +// size of client-to-client buffers in packets +#define CLIENT_PEER_FLOW_BUFFER_MIN_PACKETS 10 +// after how long of not hearing anything from the client we disconnect it +#define CLIENT_NO_DATA_TIME_LIMIT 30000 +// SO_SNDBFUF socket option for clients +#define CLIENT_DEFAULT_SOCKET_SNDBUF 16384 +// reset time when a buffer runs out or when we get the resetpeer message +#define CLIENT_RESET_TIME 30000 + +// maxiumum listen addresses +#define MAX_LISTEN_ADDRS 16 + +//#define SIMULATE_OUT_OF_CONTROL_BUFFER 20 +//#define SIMULATE_OUT_OF_FLOW_BUFFER 100 + + +// performing SSL handshake +#define INITSTATUS_HANDSHAKE 1 +// waiting for clienthello +#define INITSTATUS_WAITHELLO 2 +// initialisation was complete +#define INITSTATUS_COMPLETE 3 + +#define INITSTATUS_HASLINK(status) ((status) == INITSTATUS_WAITHELLO || (status) == INITSTATUS_COMPLETE) + +struct client_data; +struct peer_know; + +struct peer_flow { + // source client + struct client_data *src_client; + // destination client + struct client_data *dest_client; + peerid_t dest_client_id; + // node in source client hash table (by destination), only when src_client != NULL + BAVLNode src_tree_node; + // node in source client list, only when src_client != NULL + LinkedList1Node src_list_node; + // node in destination client list + LinkedList1Node dest_list_node; + // output chain + int have_io; + PacketPassFairQueueFlow qflow; + PacketProtoFlow oflow; + BufferWriter *input; + int packet_len; + uint8_t *packet; + // reset timer + BTimer reset_timer; + // opposite flow + struct peer_flow *opposite; + // pair data + struct peer_know *know; + int accepted; + int resetting; +}; + +struct peer_know { + struct client_data *from; + struct client_data *to; + int relay_server; + int relay_client; + LinkedList1Node from_node; + LinkedList1Node to_node; + BPending inform_job; + BPending uninform_job; +}; + +struct client_data { + // socket + BConnection con; + BAddr addr; + + // SSL connection, if using SSL + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection sslcon; + + // initialization state + int initstatus; + + // client data if using SSL + uint8_t cert[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_len; + uint8_t cert_old[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_old_len; + char *common_name; + + // client version + int version; + + // no data timer + BTimer disconnect_timer; + + // client ID + peerid_t id; + + // node in clients linked list + LinkedList1Node list_node; + // node in clients tree (by ID) + BAVLNode tree_node; + + // knowledge lists + LinkedList1 know_out_list; + LinkedList1 know_in_list; + + // flows from us + LinkedList1 peer_out_flows_list; + BAVL peer_out_flows_tree; + + // whether it's being removed + int dying; + BPending dying_job; + + // input + PacketProtoDecoder input_decoder; + PacketPassInterface input_interface; + + // output common + PacketStreamSender output_sender; + PacketPassPriorityQueue output_priorityqueue; + + // output control flow + PacketPassPriorityQueueFlow output_control_qflow; + PacketProtoFlow output_control_oflow; + BufferWriter *output_control_input; + int output_control_packet_len; + uint8_t *output_control_packet; + + // output peers flow + PacketPassPriorityQueueFlow output_peers_qflow; + PacketPassFairQueue output_peers_fairqueue; + LinkedList1 output_peers_flows; +}; diff --git a/app/src/main/jni/badvpn/server_connection/CMakeLists.txt b/app/src/main/jni/badvpn/server_connection/CMakeLists.txt new file mode 100644 index 0000000..965f0ed --- /dev/null +++ b/app/src/main/jni/badvpn/server_connection/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(server_conection + ServerConnection.c + SCKeepaliveSource.c +) +target_link_libraries(server_conection system flow flowextra nspr_support ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) diff --git a/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.c b/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.c new file mode 100644 index 0000000..7c6469a --- /dev/null +++ b/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.c @@ -0,0 +1,69 @@ +/** + * @file SCKeepaliveSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "SCKeepaliveSource.h" + +static void output_handler_recv (SCKeepaliveSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + struct sc_header header; + header.type = htol8(SCID_KEEPALIVE); + memcpy(data, &header, sizeof(header)); + + PacketRecvInterface_Done(&o->output, sizeof(struct sc_header)); +} + +void SCKeepaliveSource_Init (SCKeepaliveSource *o, BPendingGroup *pg) +{ + // init output + PacketRecvInterface_Init(&o->output, sizeof(struct sc_header), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void SCKeepaliveSource_Free (SCKeepaliveSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SCKeepaliveSource_GetOutput (SCKeepaliveSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.h b/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.h new file mode 100644 index 0000000..1c0951e --- /dev/null +++ b/app/src/main/jni/badvpn/server_connection/SCKeepaliveSource.h @@ -0,0 +1,72 @@ +/** + * @file SCKeepaliveSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} source which provides SCProto keepalive packets. + */ + +#ifndef BADVPN_SCKEEPALIVESOURCE_H +#define BADVPN_SCKEEPALIVESOURCE_H + +#include +#include + +/** + * A {@link PacketRecvInterface} source which provides SCProto keepalive packets. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface output; +} SCKeepaliveSource; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void SCKeepaliveSource_Init (SCKeepaliveSource *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SCKeepaliveSource_Free (SCKeepaliveSource *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be sizeof(struct sc_header). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SCKeepaliveSource_GetOutput (SCKeepaliveSource *o); + +#endif diff --git a/app/src/main/jni/badvpn/server_connection/ServerConnection.c b/app/src/main/jni/badvpn/server_connection/ServerConnection.c new file mode 100644 index 0000000..f07e224 --- /dev/null +++ b/app/src/main/jni/badvpn/server_connection/ServerConnection.c @@ -0,0 +1,669 @@ +/** + * @file ServerConnection.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define STATE_CONNECTING 1 +#define STATE_WAITINIT 2 +#define STATE_COMPLETE 3 + +static void report_error (ServerConnection *o); +static void connector_handler (ServerConnection *o, int is_error); +static void pending_handler (ServerConnection *o); +static SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey); +static void connection_handler (ServerConnection *o, int event); +static void sslcon_handler (ServerConnection *o, int event); +static void decoder_handler_error (ServerConnection *o); +static void input_handler_send (ServerConnection *o, uint8_t *data, int data_len); +static void packet_hello (ServerConnection *o, uint8_t *data, int data_len); +static void packet_newclient (ServerConnection *o, uint8_t *data, int data_len); +static void packet_endclient (ServerConnection *o, uint8_t *data, int data_len); +static void packet_inmsg (ServerConnection *o, uint8_t *data, int data_len); +static int start_packet (ServerConnection *o, void **data, int len); +static void end_packet (ServerConnection *o, uint8_t type); +static void newclient_job_handler (ServerConnection *o); + +void report_error (ServerConnection *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +void connector_handler (ServerConnection *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_CONNECTING) + ASSERT(!o->buffers_released) + + // check connection attempt result + if (is_error) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + BLog(BLOG_NOTICE, "connected"); + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->con); + BConnection_RecvAsync_Init(&o->con); + + StreamPassInterface *send_iface = BConnection_SendAsync_GetIf(&o->con); + StreamRecvInterface *recv_iface = BConnection_RecvAsync_GetIf(&o->con); + + if (o->have_ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&o->bottom_prfd, send_iface, recv_iface, o->twd, o->ssl_flags)) { + BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail0a; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->bottom_prfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&o->bottom_prfd) == PR_SUCCESS) + goto fail0a; + } + + // set client mode + if (SSL_ResetHandshake(o->ssl_prfd, PR_FALSE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail1; + } + + // set server name + if (SSL_SetURL(o->ssl_prfd, o->server_name) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_SetURL failed"); + goto fail1; + } + + // set client certificate callback + if (SSL_GetClientAuthDataHook(o->ssl_prfd, (SSLGetClientAuthData)client_auth_data_callback, o) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail1; + } + + // init BSSLConnection + BSSLConnection_Init(&o->sslcon, o->ssl_prfd, 0, BReactor_PendingGroup(o->reactor), o, (BSSLConnection_handler)sslcon_handler); + + send_iface = BSSLConnection_GetSendIf(&o->sslcon); + recv_iface = BSSLConnection_GetRecvIf(&o->sslcon); + } + + // init input chain + PacketPassInterface_Init(&o->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor)); + if (!PacketProtoDecoder_Init(&o->input_decoder, recv_iface, &o->input_interface, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + // set job to send hello + // this needs to be in here because hello sending must be done after sending started (so we can write into the send buffer), + // but before receiving started (so we don't get into conflict with the user sending packets) + BPending_Init(&o->start_job, BReactor_PendingGroup(o->reactor), (BPending_handler)pending_handler, o); + BPending_Set(&o->start_job); + + // init keepalive output branch + SCKeepaliveSource_Init(&o->output_ka_zero, BReactor_PendingGroup(o->reactor)); + PacketProtoEncoder_Init(&o->output_ka_encoder, SCKeepaliveSource_GetOutput(&o->output_ka_zero), BReactor_PendingGroup(o->reactor)); + + // init output common + + // init sender + PacketStreamSender_Init(&o->output_sender, send_iface, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(o->reactor)); + + // init keepalives + if (!KeepaliveIO_Init(&o->output_keepaliveio, o->reactor, PacketStreamSender_GetInput(&o->output_sender), PacketProtoEncoder_GetOutput(&o->output_ka_encoder), o->keepalive_interval)) { + BLog(BLOG_ERROR, "KeepaliveIO_Init failed"); + goto fail3; + } + + // init queue + PacketPassPriorityQueue_Init(&o->output_queue, KeepaliveIO_GetInput(&o->output_keepaliveio), BReactor_PendingGroup(o->reactor), 0); + + // init output local flow + + // init queue flow + PacketPassPriorityQueueFlow_Init(&o->output_local_qflow, &o->output_queue, 0); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init(&o->output_local_oflow, SC_MAX_ENC, o->buffer_size, PacketPassPriorityQueueFlow_GetInput(&o->output_local_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail4; + } + o->output_local_if = PacketProtoFlow_GetInput(&o->output_local_oflow); + + // have no output packet + o->output_local_packet_len = -1; + + // init output user flow + PacketPassPriorityQueueFlow_Init(&o->output_user_qflow, &o->output_queue, 1); + + // update state + o->state = STATE_WAITINIT; + + return; + +fail4: + PacketPassPriorityQueueFlow_Free(&o->output_local_qflow); + PacketPassPriorityQueue_Free(&o->output_queue); + KeepaliveIO_Free(&o->output_keepaliveio); +fail3: + PacketStreamSender_Free(&o->output_sender); + PacketProtoEncoder_Free(&o->output_ka_encoder); + SCKeepaliveSource_Free(&o->output_ka_zero); + BPending_Free(&o->start_job); + PacketProtoDecoder_Free(&o->input_decoder); +fail2: + PacketPassInterface_Free(&o->input_interface); + if (o->have_ssl) { + BSSLConnection_Free(&o->sslcon); +fail1: + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + } +fail0a: + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); +fail0: + // report error + report_error(o); +} + +void pending_handler (ServerConnection *o) +{ + ASSERT(o->state == STATE_WAITINIT) + ASSERT(!o->buffers_released) + DebugObject_Access(&o->d_obj); + + // send hello + struct sc_client_hello omsg; + void *packet; + if (!start_packet(o, &packet, sizeof(omsg))) { + BLog(BLOG_ERROR, "no buffer for hello"); + report_error(o); + return; + } + omsg.version = htol16(SC_VERSION); + memcpy(packet, &omsg, sizeof(omsg)); + end_packet(o, SCID_CLIENTHELLO); +} + +SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + ASSERT(o->have_ssl) + DebugObject_Access(&o->d_obj); + + CERTCertificate *newcert; + if (!(newcert = CERT_DupCertificate(o->client_cert))) { + return SECFailure; + } + + SECKEYPrivateKey *newkey; + if (!(newkey = SECKEY_CopyPrivateKey(o->client_key))) { + CERT_DestroyCertificate(newcert); + return SECFailure; + } + + *pRetCert = newcert; + *pRetKey = newkey; + return SECSuccess; +} + +void connection_handler (ServerConnection *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "connection closed"); + } else { + BLog(BLOG_INFO, "connection error"); + } + + report_error(o); + return; +} + +void sslcon_handler (ServerConnection *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_ssl) + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + BLog(BLOG_ERROR, "SSL error"); + + report_error(o); + return; +} + +void decoder_handler_error (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + + BLog(BLOG_ERROR, "decoder error"); + + report_error(o); + return; +} + +void input_handler_send (ServerConnection *o, uint8_t *data, int data_len) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_ENC) + DebugObject_Access(&o->d_obj); + + // accept packet + PacketPassInterface_Done(&o->input_interface); + + // parse header + if (data_len < sizeof(struct sc_header)) { + BLog(BLOG_ERROR, "packet too short (no sc header)"); + report_error(o); + return; + } + struct sc_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t type = ltoh8(header.type); + + // call appropriate handler based on packet type + switch (type) { + case SCID_SERVERHELLO: + packet_hello(o, data, data_len); + return; + case SCID_NEWCLIENT: + packet_newclient(o, data, data_len); + return; + case SCID_ENDCLIENT: + packet_endclient(o, data, data_len); + return; + case SCID_INMSG: + packet_inmsg(o, data, data_len); + return; + default: + BLog(BLOG_ERROR, "unknown packet type %d", (int)type); + report_error(o); + return; + } +} + +void packet_hello (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_WAITINIT) { + BLog(BLOG_ERROR, "hello: not expected"); + report_error(o); + return; + } + + if (data_len != sizeof(struct sc_server_hello)) { + BLog(BLOG_ERROR, "hello: invalid length"); + report_error(o); + return; + } + struct sc_server_hello msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // change state + o->state = STATE_COMPLETE; + + // report + o->handler_ready(o->user, id, msg.clientAddr); + return; +} + +void packet_newclient (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "newclient: not expected"); + report_error(o); + return; + } + + if (data_len < sizeof(struct sc_server_newclient) || data_len > sizeof(struct sc_server_newclient) + SCID_NEWCLIENT_MAX_CERT_LEN) { + BLog(BLOG_ERROR, "newclient: invalid length"); + report_error(o); + return; + } + + struct sc_server_newclient msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // schedule reporting new client + o->newclient_data = data; + o->newclient_data_len = data_len; + BPending_Set(&o->newclient_job); + + // send acceptpeer + struct sc_client_acceptpeer omsg; + void *packet; + if (!start_packet(o, &packet, sizeof(omsg))) { + BLog(BLOG_ERROR, "newclient: out of buffer for acceptpeer"); + report_error(o); + return; + } + omsg.clientid = htol16(id); + memcpy(packet, &omsg, sizeof(omsg)); + end_packet(o, SCID_ACCEPTPEER); +} + +void packet_endclient (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "endclient: not expected"); + report_error(o); + return; + } + + if (data_len != sizeof(struct sc_server_endclient)) { + BLog(BLOG_ERROR, "endclient: invalid length"); + report_error(o); + return; + } + + struct sc_server_endclient msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // report + o->handler_endclient(o->user, id); + return; +} + +void packet_inmsg (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "inmsg: not expected"); + report_error(o); + return; + } + + if (data_len < sizeof(struct sc_server_inmsg)) { + BLog(BLOG_ERROR, "inmsg: missing header"); + report_error(o); + return; + } + + if (data_len > sizeof(struct sc_server_inmsg) + SC_MAX_MSGLEN) { + BLog(BLOG_ERROR, "inmsg: too long"); + report_error(o); + return; + } + + struct sc_server_inmsg msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t peer_id = ltoh16(msg.clientid); + uint8_t *payload = data + sizeof(struct sc_server_inmsg); + int payload_len = data_len - sizeof(struct sc_server_inmsg); + + // report + o->handler_message(o->user, peer_id, payload, payload_len); + return; +} + +int start_packet (ServerConnection *o, void **data, int len) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(o->output_local_packet_len == -1) + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(data || len == 0) + + // obtain memory location + if (!BufferWriter_StartPacket(o->output_local_if, &o->output_local_packet)) { + BLog(BLOG_ERROR, "out of buffer"); + return 0; + } + + o->output_local_packet_len = len; + + if (data) { + *data = o->output_local_packet + sizeof(struct sc_header); + } + + return 1; +} + +void end_packet (ServerConnection *o, uint8_t type) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(o->output_local_packet_len >= 0) + ASSERT(o->output_local_packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = htol8(type); + memcpy(o->output_local_packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(o->output_local_if, sizeof(struct sc_header) + o->output_local_packet_len); + + o->output_local_packet_len = -1; +} + +int ServerConnection_Init ( + ServerConnection *o, + BReactor *reactor, + BThreadWorkDispatcher *twd, + BAddr addr, + int keepalive_interval, + int buffer_size, + int have_ssl, + int ssl_flags, + CERTCertificate *client_cert, + SECKEYPrivateKey *client_key, + const char *server_name, + void *user, + ServerConnection_handler_error handler_error, + ServerConnection_handler_ready handler_ready, + ServerConnection_handler_newclient handler_newclient, + ServerConnection_handler_endclient handler_endclient, + ServerConnection_handler_message handler_message +) +{ + ASSERT(keepalive_interval > 0) + ASSERT(buffer_size > 0) + ASSERT(have_ssl == 0 || have_ssl == 1) + ASSERT(!have_ssl || server_name) + + // init arguments + o->reactor = reactor; + o->twd = twd; + o->keepalive_interval = keepalive_interval; + o->buffer_size = buffer_size; + o->have_ssl = have_ssl; + if (have_ssl) { + o->ssl_flags = ssl_flags; + o->client_cert = client_cert; + o->client_key = client_key; + } + o->user = user; + o->handler_error = handler_error; + o->handler_ready = handler_ready; + o->handler_newclient = handler_newclient; + o->handler_endclient = handler_endclient; + o->handler_message = handler_message; + + o->server_name = NULL; + if (have_ssl && !(o->server_name = b_strdup(server_name))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "BConnection_AddressSupported failed"); + goto fail1; + } + + // init connector + if (!BConnector_Init(&o->connector, addr, o->reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail1; + } + + // init newclient job + BPending_Init(&o->newclient_job, BReactor_PendingGroup(o->reactor), (BPending_handler)newclient_job_handler, o); + + // set state + o->state = STATE_CONNECTING; + o->buffers_released = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + free(o->server_name); +fail0: + return 0; +} + +void ServerConnection_Free (ServerConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + if (o->state > STATE_CONNECTING) { + // allow freeing queue flows + PacketPassPriorityQueue_PrepareFree(&o->output_queue); + + // stop using any buffers before they get freed + if (o->have_ssl && !o->buffers_released) { + BSSLConnection_ReleaseBuffers(&o->sslcon); + } + + // free output user flow + PacketPassPriorityQueueFlow_Free(&o->output_user_qflow); + + // free output local flow + PacketProtoFlow_Free(&o->output_local_oflow); + PacketPassPriorityQueueFlow_Free(&o->output_local_qflow); + + // free output common + PacketPassPriorityQueue_Free(&o->output_queue); + KeepaliveIO_Free(&o->output_keepaliveio); + PacketStreamSender_Free(&o->output_sender); + + // free output keep-alive branch + PacketProtoEncoder_Free(&o->output_ka_encoder); + SCKeepaliveSource_Free(&o->output_ka_zero); + + // free job + BPending_Free(&o->start_job); + + // free input chain + PacketProtoDecoder_Free(&o->input_decoder); + PacketPassInterface_Free(&o->input_interface); + + // free SSL + if (o->have_ssl) { + BSSLConnection_Free(&o->sslcon); + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + } + + // free connection interfaces + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + + // free connection + BConnection_Free(&o->con); + } + + // free newclient job + BPending_Free(&o->newclient_job); + + // free connector + BConnector_Free(&o->connector); + + // free server name + free(o->server_name); +} + +void ServerConnection_ReleaseBuffers (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->buffers_released) + + if (o->state > STATE_CONNECTING && o->have_ssl) { + BSSLConnection_ReleaseBuffers(&o->sslcon); + } + + o->buffers_released = 1; +} + +PacketPassInterface * ServerConnection_GetSendInterface (ServerConnection *o) +{ + ASSERT(o->state == STATE_COMPLETE) + DebugError_AssertNoError(&o->d_err); + DebugObject_Access(&o->d_obj); + + return PacketPassPriorityQueueFlow_GetInput(&o->output_user_qflow); +} + +void newclient_job_handler (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_COMPLETE) + + struct sc_server_newclient msg; + memcpy(&msg, o->newclient_data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + int flags = ltoh16(msg.flags); + + uint8_t *cert_data = o->newclient_data + sizeof(msg); + int cert_len = o->newclient_data_len - sizeof(msg); + + // report new client + o->handler_newclient(o->user, id, flags, cert_data, cert_len); + return; +} diff --git a/app/src/main/jni/badvpn/server_connection/ServerConnection.h b/app/src/main/jni/badvpn/server_connection/ServerConnection.h new file mode 100644 index 0000000..1245c84 --- /dev/null +++ b/app/src/main/jni/badvpn/server_connection/ServerConnection.h @@ -0,0 +1,289 @@ +/** + * @file ServerConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used to communicate with a VPN chat server. + */ + +#ifndef BADVPN_SERVERCONNECTION_SERVERCONNECTION_H +#define BADVPN_SERVERCONNECTION_SERVERCONNECTION_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler function invoked when an error occurs. + * The object must be freed from withing this function. + * + * @param user value passed to {@link ServerConnection_Init} + */ +typedef void (*ServerConnection_handler_error) (void *user); + +/** + * Handler function invoked when the server becomes ready, i.e. + * the hello packet has been received. + * The object was in not ready state before. + * The object enters ready state before the handler is invoked. + * + * @param user value passed to {@link ServerConnection_Init} + * @param my_id our ID as reported by the server + * @param ext_ip the clientAddr field in the server's hello packet + */ +typedef void (*ServerConnection_handler_ready) (void *user, peerid_t my_id, uint32_t ext_ip); + +/** + * Handler function invoked when a newclient packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer + * @param flags flags field from the newclient message + * @param cert peer's certificate (if any) + * @param cert_len certificate length. Will be >=0. + */ +typedef void (*ServerConnection_handler_newclient) (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); + +/** + * Handler function invoked when an enclient packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer + */ +typedef void (*ServerConnection_handler_endclient) (void *user, peerid_t peer_id); + +/** + * Handler function invoked when an inmsg packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer from which the message came + * @param data message payload + * @param data_len message length. Will be >=0. + */ +typedef void (*ServerConnection_handler_message) (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +/** + * Object used to communicate with a VPN chat server. + */ +typedef struct { + // global resources + BReactor *reactor; + BThreadWorkDispatcher *twd; + + // keepalive interval + int keepalive_interval; + + // send buffer size + int buffer_size; + + // whether we use SSL + int have_ssl; + + // ssl flags + int ssl_flags; + + // client certificate if using SSL + CERTCertificate *client_cert; + + // client private key if using SSL + SECKEYPrivateKey *client_key; + + // server name if using SSL + char *server_name; + + // handlers + void *user; + ServerConnection_handler_error handler_error; + ServerConnection_handler_ready handler_ready; + ServerConnection_handler_newclient handler_newclient; + ServerConnection_handler_endclient handler_endclient; + ServerConnection_handler_message handler_message; + + // socket + BConnector connector; + BConnection con; + + // job to report new client after sending acceptpeer + BPending newclient_job; + uint8_t *newclient_data; + int newclient_data_len; + + // state + int state; + int buffers_released; + + // whether an error is being reported + int error; + + // defined when state > SERVERCONNECTION_STATE_CONNECTING + + // SSL file descriptor, defined only if using SSL + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection sslcon; + + // input + PacketProtoDecoder input_decoder; + PacketPassInterface input_interface; + + // keepalive output branch + SCKeepaliveSource output_ka_zero; + PacketProtoEncoder output_ka_encoder; + + // output common + PacketPassPriorityQueue output_queue; + KeepaliveIO output_keepaliveio; + PacketStreamSender output_sender; + + // output local flow + int output_local_packet_len; + uint8_t *output_local_packet; + BufferWriter *output_local_if; + PacketProtoFlow output_local_oflow; + PacketPassPriorityQueueFlow output_local_qflow; + + // output user flow + PacketPassPriorityQueueFlow output_user_qflow; + + // job to start client I/O + BPending start_job; + + DebugError d_err; + DebugObject d_obj; +} ServerConnection; + +/** + * Initializes the object. + * The object is initialized in not ready state. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSSLConnection_GlobalInit} must have been done if using SSL. + * + * @param o the object + * @param reactor {@link BReactor} we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param addr address to connect to + * @param keepalive_interval keep-alive sending interval. Must be >0. + * @param buffer_size minimum size of send buffer in number of packets. Must be >0. + * @param have_ssl whether to use SSL for connecting to the server. Must be 1 or 0. + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param client_cert if using SSL, client certificate to use. Must remain valid as + * long as this object is alive. + * @param client_key if using SSL, prvate ket to use. Must remain valid as + * long as this object is alive. + * @param server_name if using SSL, the name of the server. The string is copied. + * @param user value passed to callback functions + * @param handler_error error handler. The object must be freed from within the error + * handler before doing anything else with this object. + * @param handler_ready handler when the server becomes ready, i.e. the hello message has + * been received. + * @param handler_newclient handler when a newclient message has been received + * @param handler_endclient handler when an endclient message has been received + * @param handler_message handler when a peer message has been reveived + * @return 1 on success, 0 on failure + */ +int ServerConnection_Init ( + ServerConnection *o, + BReactor *reactor, + BThreadWorkDispatcher *twd, + BAddr addr, + int keepalive_interval, + int buffer_size, + int have_ssl, + int ssl_flags, + CERTCertificate *client_cert, + SECKEYPrivateKey *client_key, + const char *server_name, + void *user, + ServerConnection_handler_error handler_error, + ServerConnection_handler_ready handler_ready, + ServerConnection_handler_newclient handler_newclient, + ServerConnection_handler_endclient handler_endclient, + ServerConnection_handler_message handler_message +) WARN_UNUSED; + +/** + * Frees the object. + * {@link ServerConnection_ReleaseBuffers} must have been called if the + * send interface obtained from {@link ServerConnection_GetSendInterface} + * was used. + * + * @param o the object + */ +void ServerConnection_Free (ServerConnection *o); + +/** + * Stops using any buffers passed to the send interface obtained from + * {@link ServerConnection_GetSendInterface}. If the send interface + * has been used, this must be called at appropriate time before this + * object is freed. + */ +void ServerConnection_ReleaseBuffers (ServerConnection *o); + +/** + * Returns an interface for sending data to the server (just one). + * This goes directly into the link (i.e. TCP, possibly via SSL), so packets + * need to be manually encoded according to PacketProto. + * The interface must not be used after an error was reported. + * The object must be in ready state. + * Must not be called from the error handler. + * + * @param o the object + * @return the interface + */ +PacketPassInterface * ServerConnection_GetSendInterface (ServerConnection *o); + +#endif diff --git a/app/src/main/jni/badvpn/socksclient/BSocksClient.c b/app/src/main/jni/badvpn/socksclient/BSocksClient.c new file mode 100644 index 0000000..21415af --- /dev/null +++ b/app/src/main/jni/badvpn/socksclient/BSocksClient.c @@ -0,0 +1,608 @@ +/** + * @file BSocksClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#define STATE_CONNECTING 1 +#define STATE_SENDING_HELLO 2 +#define STATE_SENT_HELLO 3 +#define STATE_SENDING_PASSWORD 10 +#define STATE_SENT_PASSWORD 11 +#define STATE_SENDING_REQUEST 4 +#define STATE_SENT_REQUEST 5 +#define STATE_RECEIVED_REPLY_HEADER 6 +#define STATE_UP 7 + +static void report_error (BSocksClient *o, int error); +static void init_control_io (BSocksClient *o); +static void free_control_io (BSocksClient *o); +static void init_up_io (BSocksClient *o); +static void free_up_io (BSocksClient *o); +static int reserve_buffer (BSocksClient *o, bsize_t size); +static void start_receive (BSocksClient *o, uint8_t *dest, int total); +static void do_receive (BSocksClient *o); +static void connector_handler (BSocksClient* o, int is_error); +static void connection_handler (BSocksClient* o, int event); +static void recv_handler_done (BSocksClient *o, int data_len); +static void send_handler_done (BSocksClient *o); +static void auth_finished (BSocksClient *p); + +void report_error (BSocksClient *o, int error) +{ + DEBUGERROR(&o->d_err, o->handler(o->user, error)) +} + +void init_control_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + o->control.recv_if = BConnection_RecvAsync_GetIf(&o->con); + StreamRecvInterface_Receiver_Init(o->control.recv_if, (StreamRecvInterface_handler_done)recv_handler_done, o); + + // init sending + BConnection_SendAsync_Init(&o->con); + PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), INT_MAX, BReactor_PendingGroup(o->reactor)); + o->control.send_if = PacketStreamSender_GetInput(&o->control.send_sender); + PacketPassInterface_Sender_Init(o->control.send_if, (PacketPassInterface_handler_done)send_handler_done, o); +} + +void free_control_io (BSocksClient *o) +{ + // free sending + PacketStreamSender_Free(&o->control.send_sender); + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +void init_up_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + + // init sending + BConnection_SendAsync_Init(&o->con); +} + +void free_up_io (BSocksClient *o) +{ + // free sending + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +int reserve_buffer (BSocksClient *o, bsize_t size) +{ + if (size.is_overflow) { + BLog(BLOG_ERROR, "size overflow"); + return 0; + } + + char *buffer = (char *)BRealloc(o->buffer, size.value); + if (!buffer) { + BLog(BLOG_ERROR, "BRealloc failed"); + return 0; + } + + o->buffer = buffer; + + return 1; +} + +void start_receive (BSocksClient *o, uint8_t *dest, int total) +{ + ASSERT(total > 0) + + o->control.recv_dest = dest; + o->control.recv_len = 0; + o->control.recv_total = total; + + do_receive(o); +} + +void do_receive (BSocksClient *o) +{ + ASSERT(o->control.recv_len < o->control.recv_total) + + StreamRecvInterface_Receiver_Recv(o->control.recv_if, o->control.recv_dest + o->control.recv_len, o->control.recv_total - o->control.recv_len); +} + +void connector_handler (BSocksClient* o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_CONNECTING) + + // check connection result + if (is_error) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + BLog(BLOG_DEBUG, "connected"); + + // init control I/O + init_control_io(o); + + // check number of methods + if (o->num_auth_info == 0 || o->num_auth_info > 255) { + BLog(BLOG_ERROR, "invalid number of authentication methods"); + goto fail1; + } + + // allocate buffer for sending hello + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_client_hello_header)), + bsize_mul( + bsize_fromsize(o->num_auth_info), + bsize_fromsize(sizeof(struct socks_client_hello_method)) + ) + ); + if (!reserve_buffer(o, size)) { + goto fail1; + } + + // write hello header + struct socks_client_hello_header header; + header.ver = hton8(SOCKS_VERSION); + header.nmethods = hton8(o->num_auth_info); + memcpy(o->buffer, &header, sizeof(header)); + + // write hello methods + for (size_t i = 0; i < o->num_auth_info; i++) { + struct socks_client_hello_method method; + method.method = hton8(o->auth_info[i].auth_type); + memcpy(o->buffer + sizeof(header) + i * sizeof(method), &method, sizeof(method)); + } + + // send + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_HELLO; + + return; + +fail1: + free_control_io(o); + BConnection_Free(&o->con); +fail0: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void connection_handler (BSocksClient* o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state != STATE_CONNECTING) + + if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED); + return; + } + + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void recv_handler_done (BSocksClient *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->control.recv_total - o->control.recv_len) + DebugObject_Access(&o->d_obj); + + o->control.recv_len += data_len; + + if (o->control.recv_len < o->control.recv_total) { + do_receive(o); + return; + } + + switch (o->state) { + case STATE_SENT_HELLO: { + BLog(BLOG_DEBUG, "received hello"); + + struct socks_server_hello imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + size_t auth_index; + for (auth_index = 0; auth_index < o->num_auth_info; auth_index++) { + if (o->auth_info[auth_index].auth_type == ntoh8(imsg.method)) { + break; + } + } + + if (auth_index == o->num_auth_info) { + BLog(BLOG_NOTICE, "server didn't accept any authentication method"); + goto fail; + } + + const struct BSocksClient_auth_info *ai = &o->auth_info[auth_index]; + + switch (ai->auth_type) { + case SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED: { + BLog(BLOG_DEBUG, "no authentication"); + + auth_finished(o); + } break; + + case SOCKS_METHOD_USERNAME_PASSWORD: { + BLog(BLOG_DEBUG, "password authentication"); + + if (ai->password.username_len == 0 || ai->password.username_len > 255 || + ai->password.password_len == 0 || ai->password.password_len > 255 + ) { + BLog(BLOG_NOTICE, "invalid username/password length"); + goto fail; + } + + // allocate password packet + bsize_t size = bsize_fromsize(1 + 1 + ai->password.username_len + 1 + ai->password.password_len); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // write password packet + char *ptr = o->buffer; + *ptr++ = 1; + *ptr++ = ai->password.username_len; + memcpy(ptr, ai->password.username, ai->password.username_len); + ptr += ai->password.username_len; + *ptr++ = ai->password.password_len; + memcpy(ptr, ai->password.password, ai->password.password_len); + ptr += ai->password.password_len; + + // start sending + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_PASSWORD; + } break; + + default: ASSERT(0); + } + } break; + + case STATE_SENT_REQUEST: { + BLog(BLOG_DEBUG, "received reply header"); + + struct socks_reply_header imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + if (ntoh8(imsg.rep) != SOCKS_REP_SUCCEEDED) { + BLog(BLOG_NOTICE, "reply not successful"); + goto fail; + } + + int addr_len; + switch (ntoh8(imsg.atyp)) { + case SOCKS_ATYP_IPV4: + addr_len = sizeof(struct socks_addr_ipv4); + break; + case SOCKS_ATYP_IPV6: + addr_len = sizeof(struct socks_addr_ipv6); + break; + default: + BLog(BLOG_NOTICE, "reply has unknown address type"); + goto fail; + } + + // receive the rest of the reply + start_receive(o, (uint8_t *)o->buffer + sizeof(imsg), addr_len); + + // set state + o->state = STATE_RECEIVED_REPLY_HEADER; + } break; + + case STATE_SENT_PASSWORD: { + BLog(BLOG_DEBUG, "received password reply"); + + if (o->buffer[0] != 1) { + BLog(BLOG_NOTICE, "password reply has unknown version"); + goto fail; + } + + if (o->buffer[1] != 0) { + BLog(BLOG_NOTICE, "password reply is negative"); + goto fail; + } + + auth_finished(o); + } break; + + case STATE_RECEIVED_REPLY_HEADER: { + BLog(BLOG_DEBUG, "received reply rest"); + + // free buffer + BFree(o->buffer); + o->buffer = NULL; + + // free control I/O + free_control_io(o); + + // init up I/O + init_up_io(o); + + // set state + o->state = STATE_UP; + + // call handler + o->handler(o->user, BSOCKSCLIENT_EVENT_UP); + return; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void send_handler_done (BSocksClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buffer) + + switch (o->state) { + case STATE_SENDING_HELLO: { + BLog(BLOG_DEBUG, "sent hello"); + + // allocate buffer for receiving hello + bsize_t size = bsize_fromsize(sizeof(struct socks_server_hello)); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive hello + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_HELLO; + } break; + + case STATE_SENDING_REQUEST: { + BLog(BLOG_DEBUG, "sent request"); + + // allocate buffer for receiving reply + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_reply_header)), + bsize_max(bsize_fromsize(sizeof(struct socks_addr_ipv4)), bsize_fromsize(sizeof(struct socks_addr_ipv6))) + ); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, sizeof(struct socks_reply_header)); + + // set state + o->state = STATE_SENT_REQUEST; + } break; + + case STATE_SENDING_PASSWORD: { + BLog(BLOG_DEBUG, "send password"); + + // allocate buffer for receiving reply + bsize_t size = bsize_fromsize(2); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_PASSWORD; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void auth_finished (BSocksClient *o) +{ + // allocate request buffer + bsize_t size = bsize_fromsize(sizeof(struct socks_request_header)); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4))); break; + case BADDR_TYPE_IPV6: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6))); break; + } + if (!reserve_buffer(o, size)) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; + } + + // write request + struct socks_request_header header; + header.ver = hton8(SOCKS_VERSION); + header.cmd = hton8(SOCKS_CMD_CONNECT); + header.rsv = hton8(0); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: { + header.atyp = hton8(SOCKS_ATYP_IPV4); + struct socks_addr_ipv4 addr; + addr.addr = o->dest_addr.ipv4.ip; + addr.port = o->dest_addr.ipv4.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + case BADDR_TYPE_IPV6: { + header.atyp = hton8(SOCKS_ATYP_IPV6); + struct socks_addr_ipv6 addr; + memcpy(addr.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip)); + addr.port = o->dest_addr.ipv6.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + default: + ASSERT(0); + } + memcpy(o->buffer, &header, sizeof(header)); + + // send request + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_REQUEST; +} + +struct BSocksClient_auth_info BSocksClient_auth_none (void) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED; + return info; +} + +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_USERNAME_PASSWORD; + info.password.username = username; + info.password.username_len = username_len; + info.password.password = password; + info.password.password_len = password_len; + return info; +} + +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) +{ + ASSERT(!BAddr_IsInvalid(&server_addr)) + ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6) +#ifndef NDEBUG + for (size_t i = 0; i < num_auth_info; i++) { + ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED || + auth_info[i].auth_type == SOCKS_METHOD_USERNAME_PASSWORD) + } +#endif + + // init arguments + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->dest_addr = dest_addr; + o->handler = handler; + o->user = user; + o->reactor = reactor; + + // set no buffer + o->buffer = NULL; + + // init connector + if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail0; + } + + // set state + o->state = STATE_CONNECTING; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void BSocksClient_Free (BSocksClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + if (o->state != STATE_CONNECTING) { + if (o->state == STATE_UP) { + // free up I/O + free_up_io(o); + } else { + // free control I/O + free_control_io(o); + } + + // free connection + BConnection_Free(&o->con); + } + + // free connector + BConnector_Free(&o->connector); + + // free buffer + if (o->buffer) { + BFree(o->buffer); + } +} + +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_SendAsync_GetIf(&o->con); +} + +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_RecvAsync_GetIf(&o->con); +} diff --git a/app/src/main/jni/badvpn/socksclient/BSocksClient.h b/app/src/main/jni/badvpn/socksclient/BSocksClient.h new file mode 100644 index 0000000..f19b3a8 --- /dev/null +++ b/app/src/main/jni/badvpn/socksclient/BSocksClient.h @@ -0,0 +1,147 @@ +/** + * @file BSocksClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * SOCKS5 client. TCP only, no authentication. + */ + +#ifndef BADVPN_SOCKS_BSOCKSCLIENT_H +#define BADVPN_SOCKS_BSOCKSCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BSOCKSCLIENT_EVENT_ERROR 1 +#define BSOCKSCLIENT_EVENT_UP 2 +#define BSOCKSCLIENT_EVENT_ERROR_CLOSED 3 + +/** + * Handler for events generated by the SOCKS client. + * + * @param user as in {@link BSocksClient_Init} + * @param event event type. One of BSOCKSCLIENT_EVENT_ERROR, BSOCKSCLIENT_EVENT_UP + * and BSOCKSCLIENT_EVENT_ERROR_CLOSED. + * If event is BSOCKSCLIENT_EVENT_UP, the object was previously in down + * state and has transitioned to up state; I/O can be done from this point on. + * If event is BSOCKSCLIENT_EVENT_ERROR or BSOCKSCLIENT_EVENT_ERROR_CLOSED, + * the object must be freed from within the job closure of this handler, + * and no further I/O must be attempted. + */ +typedef void (*BSocksClient_handler) (void *user, int event); + +struct BSocksClient_auth_info { + int auth_type; + union { + struct { + const char *username; + size_t username_len; + const char *password; + size_t password_len; + } password; + }; +}; + +typedef struct { + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr dest_addr; + BSocksClient_handler handler; + void *user; + BReactor *reactor; + int state; + char *buffer; + BConnector connector; + BConnection con; + union { + struct { + PacketPassInterface *send_if; + PacketStreamSender send_sender; + StreamRecvInterface *recv_if; + uint8_t *recv_dest; + int recv_len; + int recv_total; + } control; + }; + DebugError d_err; + DebugObject d_obj; +} BSocksClient; + +struct BSocksClient_auth_info BSocksClient_auth_none (void); +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len); + +/** + * Initializes the object. + * The object is initialized in down state. The object must transition to up + * state before the user may begin any I/O. + * + * @param o the object + * @param server_addr SOCKS5 server address + * @param dest_addr remote address + * @param handler handler for up and error events + * @param user value passed to handler + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void BSocksClient_Free (BSocksClient *o); + +/** + * Returns the send interface. + * The object must be in up state. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o); + +/** + * Returns the receive interface. + * The object must be in up state. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o); + +#endif diff --git a/app/src/main/jni/badvpn/socksclient/CMakeLists.txt b/app/src/main/jni/badvpn/socksclient/CMakeLists.txt new file mode 100644 index 0000000..d798194 --- /dev/null +++ b/app/src/main/jni/badvpn/socksclient/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(socksclient + BSocksClient.c +) +target_link_libraries(socksclient system flow flowextra) diff --git a/app/src/main/jni/badvpn/stringmap/BStringMap.c b/app/src/main/jni/badvpn/stringmap/BStringMap.c new file mode 100644 index 0000000..d53f10e --- /dev/null +++ b/app/src/main/jni/badvpn/stringmap/BStringMap.c @@ -0,0 +1,198 @@ +/** + * @file BStringMap.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +static int string_comparator (void *unused, char **str1, char **str2) +{ + int c = strcmp(*str1, *str2); + return B_COMPARE(c, 0); +} + +static void free_entry (BStringMap *o, struct BStringMap_entry *e) +{ + BAVL_Remove(&o->tree, &e->tree_node); + free(e->value); + free(e->key); + free(e); +} + +void BStringMap_Init (BStringMap *o) +{ + // init tree + BAVL_Init(&o->tree, OFFSET_DIFF(struct BStringMap_entry, key, tree_node), (BAVL_comparator)string_comparator, NULL); + + DebugObject_Init(&o->d_obj); +} + +int BStringMap_InitCopy (BStringMap *o, const BStringMap *src) +{ + BStringMap_Init(o); + + const char *key = BStringMap_First(src); + while (key) { + if (!BStringMap_Set(o, key, BStringMap_Get(src, key))) { + goto fail1; + } + key = BStringMap_Next(src, key); + } + + return 1; + +fail1: + BStringMap_Free(o); + return 0; +} + +void BStringMap_Free (BStringMap *o) +{ + DebugObject_Free(&o->d_obj); + + // free entries + BAVLNode *tree_node; + while (tree_node = BAVL_GetFirst(&o->tree)) { + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + free_entry(o, e); + } +} + +const char * BStringMap_Get (const BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + + // lookup + BAVLNode *tree_node = BAVL_LookupExact(&o->tree, &key); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return e->value; +} + +int BStringMap_Set (BStringMap *o, const char *key, const char *value) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + ASSERT(value) + + // alloc entry + struct BStringMap_entry *e = malloc(sizeof(*e)); + if (!e) { + goto fail0; + } + + // alloc and set key + if (!(e->key = malloc(strlen(key) + 1))) { + goto fail1; + } + strcpy(e->key, key); + + // alloc and set value + if (!(e->value = malloc(strlen(value) + 1))) { + goto fail2; + } + strcpy(e->value, value); + + // try inserting to tree + BAVLNode *ex_tree_node; + if (!BAVL_Insert(&o->tree, &e->tree_node, &ex_tree_node)) { + // remove existing entry + struct BStringMap_entry *ex_e = UPPER_OBJECT(ex_tree_node, struct BStringMap_entry, tree_node); + free_entry(o, ex_e); + + // insert new node + ASSERT_EXECUTE(BAVL_Insert(&o->tree, &e->tree_node, NULL)) + } + + return 1; + +fail2: + free(e->key); +fail1: + free(e); +fail0: + return 0; +} + +void BStringMap_Unset (BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + + // lookup + BAVLNode *tree_node = BAVL_LookupExact(&o->tree, &key); + if (!tree_node) { + return; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + // remove + free_entry(o, e); +} + +const char * BStringMap_First (const BStringMap *o) +{ + DebugObject_Access(&o->d_obj); + + // get first + BAVLNode *tree_node = BAVL_GetFirst(&o->tree); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return e->key; +} + +const char * BStringMap_Next (const BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + ASSERT(BAVL_LookupExact(&o->tree, &key)) + + // get entry + struct BStringMap_entry *e = UPPER_OBJECT(BAVL_LookupExact(&o->tree, &key), struct BStringMap_entry, tree_node); + + // get next + BAVLNode *tree_node = BAVL_GetNext(&o->tree, &e->tree_node); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *next_e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return next_e->key; +} diff --git a/app/src/main/jni/badvpn/stringmap/BStringMap.h b/app/src/main/jni/badvpn/stringmap/BStringMap.h new file mode 100644 index 0000000..39b2071 --- /dev/null +++ b/app/src/main/jni/badvpn/stringmap/BStringMap.h @@ -0,0 +1,57 @@ +/** + * @file BStringMap.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STRINGMAP_BSTRINGMAP_H +#define BADVPN_STRINGMAP_BSTRINGMAP_H + +#include +#include +#include + +struct BStringMap_entry { + char *key; + char *value; + BAVLNode tree_node; +}; + +typedef struct { + BAVL tree; + DebugObject d_obj; +} BStringMap; + +void BStringMap_Init (BStringMap *o); +int BStringMap_InitCopy (BStringMap *o, const BStringMap *src) WARN_UNUSED; +void BStringMap_Free (BStringMap *o); +const char * BStringMap_Get (const BStringMap *o, const char *key); +int BStringMap_Set (BStringMap *o, const char *key, const char *value) WARN_UNUSED; +void BStringMap_Unset (BStringMap *o, const char *key); +const char * BStringMap_First (const BStringMap *o); +const char * BStringMap_Next (const BStringMap *o, const char *key); + +#endif diff --git a/app/src/main/jni/badvpn/stringmap/CMakeLists.txt b/app/src/main/jni/badvpn/stringmap/CMakeLists.txt new file mode 100644 index 0000000..35c4435 --- /dev/null +++ b/app/src/main/jni/badvpn/stringmap/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(stringmap + BStringMap.c +) diff --git a/app/src/main/jni/badvpn/structure/BAVL.h b/app/src/main/jni/badvpn/structure/BAVL.h new file mode 100644 index 0000000..66993b3 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/BAVL.h @@ -0,0 +1,797 @@ +/** + * @file BAVL.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * AVL tree. + */ + +#ifndef BADVPN_STRUCTURE_BAVL_H +#define BADVPN_STRUCTURE_BAVL_H + +//#define BAVL_DEBUG + +#include +#include + +#include + +/** + * Handler function called by tree algorithms to compare two values. + * For any two values, the comparator must always return the same result. + * The <= relation defined by the comparator must be a total order. + * Values are obtained like that: + * - The value of a node in the tree, or a node that is being inserted is: + * (uint8_t *)node + offset. + * - The value being looked up is the same as given to the lookup function. + * + * @param user as in {@link BAVL_Init} + * @param val1 first value + * @param val2 second value + * @return -1 if val1 < val2, 0 if val1 = val2, 1 if val1 > val2 + */ +typedef int (*BAVL_comparator) (void *user, void *val1, void *val2); + +struct BAVLNode; + +/** + * AVL tree. + */ +typedef struct { + int offset; + BAVL_comparator comparator; + void *user; + struct BAVLNode *root; +} BAVL; + +/** + * AVL tree node. + */ +typedef struct BAVLNode { + struct BAVLNode *parent; + struct BAVLNode *link[2]; + int8_t balance; +#ifdef BAVL_COUNT + uint64_t count; +#endif +} BAVLNode; + +/** + * Initializes the tree. + * + * @param o tree to initialize + * @param offset offset of a value from its node + * @param comparator value comparator handler function + * @param user value to pass to comparator + */ +static void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user); + +/** + * Inserts a node into the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node uninitialized node to insert. Must have a valid value (its value + * may be passed to the comparator during insertion). + * @param ref if not NULL, will return (regardless if insertion succeeded): + * - the greatest node lesser than the inserted value, or (not in order) + * - the smallest node greater than the inserted value, or + * - NULL meaning there were no nodes in the tree. + * @param 1 on success, 0 if an element with an equal value is already in the tree + */ +static int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref); + +/** + * Removes a node from the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node node to remove + */ +static void BAVL_Remove (BAVL *o, BAVLNode *node); + +/** + * Checks if the tree is empty. + * Must not be called from comparator. + * + * @param o the tree + * @return 1 if empty, 0 if not + */ +static int BAVL_IsEmpty (const BAVL *o); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else if the tree is not empty: + * - the greatest node lesser than the given value, or (not in order) + * - the smallest node greater than the given value. + * NULL if the tree is empty. + */ +static BAVLNode * BAVL_Lookup (const BAVL *o, void *val); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else NULL. + */ +static BAVLNode * BAVL_LookupExact (const BAVL *o, void *val); + +/** + * Returns the smallest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return smallest node or NULL + */ +static BAVLNode * BAVL_GetFirst (const BAVL *o); + +/** + * Returns the greatest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return greatest node or NULL + */ +static BAVLNode * BAVL_GetLast (const BAVL *o); + +/** + * Returns the node that follows the given node, or NULL if it's the + * last node. + * + * @param o the tree + * @param n node + * @return next node, or NULL + */ +static BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n); + +/** + * Returns the node that precedes the given node, or NULL if it's the + * first node. + * + * @param o the tree + * @param n node + * @return previous node, or NULL + */ +static BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n); + +#ifdef BAVL_COUNT +static uint64_t BAVL_Count (const BAVL *o); +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n); +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index); +#endif + +static void BAVL_Verify (BAVL *o); + +#define BAVL_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define BAVL_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) + +static void * _BAVL_node_value (const BAVL *o, BAVLNode *n) +{ + return ((uint8_t *)n + o->offset); +} + +static int _BAVL_compare_values (const BAVL *o, void *v1, void *v2) +{ + int res = o->comparator(o->user, v1, v2); + + ASSERT(res == -1 || res == 0 || res == 1) + + return res; +} + +static int _BAVL_compare_nodes (BAVL *o, BAVLNode *n1, BAVLNode *n2) +{ + return _BAVL_compare_values(o, _BAVL_node_value(o, n1), _BAVL_node_value(o, n2)); +} + +#ifdef BAVL_AUTO_VERIFY +#define BAVL_ASSERT(_h) BAVL_Verify((_h)); +#else +#define BAVL_ASSERT(_h) +#endif + +static int _BAVL_assert_recurser (BAVL *o, BAVLNode *n) +{ + ASSERT_FORCE(n->balance >= -1) + ASSERT_FORCE(n->balance <= 1) + + int height_left = 0; + int height_right = 0; +#ifdef BAVL_COUNT + uint64_t count_left = 0; + uint64_t count_right = 0; +#endif + + // check left subtree + if (n->link[0]) { + // check parent link + ASSERT_FORCE(n->link[0]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[0], n) == -1) + // recursively calculate height + height_left = _BAVL_assert_recurser(o, n->link[0]); +#ifdef BAVL_COUNT + count_left = n->link[0]->count; +#endif + } + + // check right subtree + if (n->link[1]) { + // check parent link + ASSERT_FORCE(n->link[1]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[1], n) == 1) + // recursively calculate height + height_right = _BAVL_assert_recurser(o, n->link[1]); +#ifdef BAVL_COUNT + count_right = n->link[1]->count; +#endif + } + + // check balance factor + ASSERT_FORCE(n->balance == height_right - height_left) + +#ifdef BAVL_COUNT + // check count + ASSERT_FORCE(n->count == 1 + count_left + count_right) +#endif + + return (BAVL_MAX(height_left, height_right) + 1); +} + +#ifdef BAVL_COUNT +static void _BAVL_update_count_from_children (BAVLNode *n) +{ + n->count = 1 + (n->link[0] ? n->link[0]->count : 0) + (n->link[1] ? n->link[1]->count : 0); +} +#endif + +static void _BAVL_rotate (BAVL *tree, BAVLNode *r, uint8_t dir) +{ + BAVLNode *nr = r->link[!dir]; + + r->link[!dir] = nr->link[dir]; + if (r->link[!dir]) { + r->link[!dir]->parent = r; + } + nr->link[dir] = r; + nr->parent = r->parent; + if (nr->parent) { + nr->parent->link[r == r->parent->link[1]] = nr; + } else { + tree->root = nr; + } + r->parent = nr; + +#ifdef BAVL_COUNT + // update counts + _BAVL_update_count_from_children(r); // first r! + _BAVL_update_count_from_children(nr); +#endif +} + +static BAVLNode * _BAVL_subtree_max (BAVLNode *n) +{ + ASSERT(n) + while (n->link[1]) { + n = n->link[1]; + } + return n; +} + +static void _BAVL_replace_subtree (BAVL *tree, BAVLNode *dest, BAVLNode *n) +{ + ASSERT(dest) + + if (dest->parent) { + dest->parent->link[dest == dest->parent->link[1]] = n; + } else { + tree->root = n; + } + if (n) { + n->parent = dest->parent; + } + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *c = dest->parent; c; c = c->parent) { + ASSERT(c->count >= dest->count) + c->count -= dest->count; + if (n) { + ASSERT(n->count <= UINT64_MAX - c->count) + c->count += n->count; + } + } +#endif +} + +static void _BAVL_swap_nodes (BAVL *tree, BAVLNode *n1, BAVLNode *n2) +{ + if (n2->parent == n1 || n1->parent == n2) { + // when the nodes are directly connected we need special handling + // make sure n1 is above n2 + if (n1->parent == n2) { + BAVLNode *t = n1; + n1 = n2; + n2 = t; + } + + uint8_t side = (n2 == n1->link[1]); + BAVLNode *c = n1->link[!side]; + + if (n1->link[0] = n2->link[0]) { + n1->link[0]->parent = n1; + } + if (n1->link[1] = n2->link[1]) { + n1->link[1]->parent = n1; + } + + if (n2->parent = n1->parent) { + n2->parent->link[n1 == n1->parent->link[1]] = n2; + } else { + tree->root = n2; + } + + n2->link[side] = n1; + n1->parent = n2; + if (n2->link[!side] = c) { + c->parent = n2; + } + } else { + BAVLNode *temp; + + // swap parents + temp = n1->parent; + if (n1->parent = n2->parent) { + n1->parent->link[n2 == n2->parent->link[1]] = n1; + } else { + tree->root = n1; + } + if (n2->parent = temp) { + n2->parent->link[n1 == temp->link[1]] = n2; + } else { + tree->root = n2; + } + + // swap left children + temp = n1->link[0]; + if (n1->link[0] = n2->link[0]) { + n1->link[0]->parent = n1; + } + if (n2->link[0] = temp) { + n2->link[0]->parent = n2; + } + + // swap right children + temp = n1->link[1]; + if (n1->link[1] = n2->link[1]) { + n1->link[1]->parent = n1; + } + if (n2->link[1] = temp) { + n2->link[1]->parent = n2; + } + } + + // swap balance factors + int8_t b = n1->balance; + n1->balance = n2->balance; + n2->balance = b; + +#ifdef BAVL_COUNT + // swap counts + uint64_t c = n1->count; + n1->count = n2->count; + n2->count = c; +#endif +} + +static void _BAVL_rebalance (BAVL *o, BAVLNode *node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(node->balance >= -1 && node->balance <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = BAVL_MAX(deltac, BAVL_OPTNEG(node->balance, side)) - BAVL_MAX(0, BAVL_OPTNEG(node->balance, side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + node->balance -= BAVL_OPTNEG(deltac, side); + + BAVLNode *child; + BAVLNode *gchild; + + // perform transformations if the balance factor is wrong + if (node->balance == 2 || node->balance == -2) { + uint8_t bside; + int8_t bsidef; + if (node->balance == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(node->link[bside]) + child = node->link[bside]; + switch (child->balance * bsidef) { + case 1: + _BAVL_rotate(o, node, !bside); + node->balance = 0; + child->balance = 0; + node = child; + delta -= 1; + break; + case 0: + _BAVL_rotate(o, node, !bside); + node->balance = 1 * bsidef; + child->balance = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(child->link[!bside]) + gchild = child->link[!bside]; + _BAVL_rotate(o, child, bside); + _BAVL_rotate(o, node, !bside); + node->balance = -BAVL_MAX(0, gchild->balance * bsidef) * bsidef; + child->balance = BAVL_MAX(0, -gchild->balance * bsidef) * bsidef; + gchild->balance = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (node->parent) { + _BAVL_rebalance(o, node->parent, node == node->parent->link[1], delta); + } +} + +void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user) +{ + o->offset = offset; + o->comparator = comparator; + o->user = user; + o->root = NULL; + + BAVL_ASSERT(o) +} + +int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref) +{ + // insert to root? + if (!o->root) { + o->root = node; + node->parent = NULL; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + + BAVL_ASSERT(o) + + if (ref) { + *ref = NULL; + } + return 1; + } + + // find node to insert to + BAVLNode *c = o->root; + int side; + while (1) { + // compare + int comp = _BAVL_compare_nodes(o, node, c); + + // have we found a node that compares equal? + if (comp == 0) { + if (ref) { + *ref = c; + } + return 0; + } + + side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + break; + } + + c = c->link[side]; + } + + // insert + c->link[side] = node; + node->parent = c; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *p = c; p; p = p->parent) { + ASSERT(p->count < UINT64_MAX) + p->count++; + } +#endif + + // rebalance + _BAVL_rebalance(o, c, side, 1); + + BAVL_ASSERT(o) + + if (ref) { + *ref = c; + } + return 1; +} + +void BAVL_Remove (BAVL *o, BAVLNode *node) +{ + // if we have both subtrees, swap the node and the largest node + // in the left subtree, so we have at most one subtree + if (node->link[0] && node->link[1]) { + // find the largest node in the left subtree + BAVLNode *max = _BAVL_subtree_max(node->link[0]); + // swap the nodes + _BAVL_swap_nodes(o, node, max); + } + + // have at most one child now + ASSERT(!(node->link[0] && node->link[1])) + + BAVLNode *parent = node->parent; + BAVLNode *child = (node->link[0] ? node->link[0] : node->link[1]); + + if (parent) { + // remember on which side node is + int side = (node == parent->link[1]); + // replace node with child + _BAVL_replace_subtree(o, node, child); + // rebalance + _BAVL_rebalance(o, parent, side, -1); + } else { + // replace node with child + _BAVL_replace_subtree(o, node, child); + } + + BAVL_ASSERT(o) +} + +int BAVL_IsEmpty (const BAVL *o) +{ + return (!o->root); +} + +BAVLNode * BAVL_Lookup (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return c; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_LookupExact (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return NULL; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_GetFirst (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[0]) { + n = n->link[0]; + } + + return n; +} + +BAVLNode * BAVL_GetLast (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[1]) { + n = n->link[1]; + } + + return n; +} + +BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n) +{ + if (n->link[1]) { + n = n->link[1]; + while (n->link[0]) { + n = n->link[0]; + } + } else { + while (n->parent && n == n->parent->link[1]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n) +{ + if (n->link[0]) { + n = n->link[0]; + while (n->link[1]) { + n = n->link[1]; + } + } else { + while (n->parent && n == n->parent->link[0]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +#ifdef BAVL_COUNT + +static uint64_t BAVL_Count (const BAVL *o) +{ + return (o->root ? o->root->count : 0); +} + +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n) +{ + uint64_t index = (n->link[0] ? n->link[0]->count : 0); + + for (BAVLNode *c = n; c->parent; c = c->parent) { + if (c == c->parent->link[1]) { + ASSERT(c->parent->count > c->count) + ASSERT(c->parent->count - c->count <= UINT64_MAX - index) + index += c->parent->count - c->count; + } + } + + return index; +} + +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index) +{ + if (index >= BAVL_Count(o)) { + return NULL; + } + + BAVLNode *c = o->root; + + while (1) { + ASSERT(c) + ASSERT(index < c->count) + + uint64_t left_count = (c->link[0] ? c->link[0]->count : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = c->link[0]; + } else { + c = c->link[1]; + index -= left_count + 1; + } + } +} + +#endif + +static void BAVL_Verify (BAVL *o) +{ + if (o->root) { + ASSERT(!o->root->parent) + _BAVL_assert_recurser(o, o->root); + } +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/CAvl.h b/app/src/main/jni/badvpn/structure/CAvl.h new file mode 100644 index 0000000..ea33a3e --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CAvl.h @@ -0,0 +1,36 @@ +/** + * @file CAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CAVL_H +#define BADVPN_CAVL_H + +#include +#include + +#endif diff --git a/app/src/main/jni/badvpn/structure/CAvl_decl.h b/app/src/main/jni/badvpn/structure/CAvl_decl.h new file mode 100644 index 0000000..7d54a81 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CAvl_decl.h @@ -0,0 +1,77 @@ +/** + * @file CAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +typedef struct { + CAvlLink root; +} CAvl; + +typedef struct { + CAvlEntry *ptr; + CAvlLink link; +} CAvlRef; + +static int CAvlIsNullRef (CAvlRef node); +static int CAvlIsValidRef (CAvlRef node); +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link); + +static void CAvl_Init (CAvl *o); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref); +#else +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index); +#endif +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +#endif +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node); +static int CAvl_IsEmpty (const CAvl *o); +static void CAvl_Verify (const CAvl *o, CAvlArg arg); +#if CAVL_PARAM_FEATURE_COUNTS +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg); +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index); +#endif +#if CAVL_PARAM_FEATURE_ASSOC +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg); +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user); +#endif + +#include "CAvl_footer.h" diff --git a/app/src/main/jni/badvpn/structure/CAvl_footer.h b/app/src/main/jni/badvpn/structure/CAvl_footer.h new file mode 100644 index 0000000..43b85c3 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CAvl_footer.h @@ -0,0 +1,113 @@ +/** + * @file CAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef CAVL_PARAM_NAME +#undef CAVL_PARAM_FEATURE_COUNTS +#undef CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +#undef CAVL_PARAM_FEATURE_NOKEYS +#undef CAVL_PARAM_FEATURE_ASSOC +#undef CAVL_PARAM_TYPE_ENTRY +#undef CAVL_PARAM_TYPE_LINK +#undef CAVL_PARAM_TYPE_KEY +#undef CAVL_PARAM_TYPE_ARG +#undef CAVL_PARAM_TYPE_COUNT +#undef CAVL_PARAM_TYPE_ASSOC +#undef CAVL_PARAM_VALUE_COUNT_MAX +#undef CAVL_PARAM_VALUE_NULL +#undef CAVL_PARAM_VALUE_ASSOC_ZERO +#undef CAVL_PARAM_FUN_DEREF +#undef CAVL_PARAM_FUN_COMPARE_ENTRIES +#undef CAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef CAVL_PARAM_FUN_ASSOC_VALUE +#undef CAVL_PARAM_FUN_ASSOC_OPER +#undef CAVL_PARAM_MEMBER_CHILD +#undef CAVL_PARAM_MEMBER_BALANCE +#undef CAVL_PARAM_MEMBER_PARENT +#undef CAVL_PARAM_MEMBER_COUNT +#undef CAVL_PARAM_MEMBER_ASSOC + +#undef CAvl +#undef CAvlEntry +#undef CAvlLink +#undef CAvlRef +#undef CAvlArg +#undef CAvlKey +#undef CAvlCount +#undef CAvlAssoc + +#undef CAvlIsNullRef +#undef CAvlIsValidRef +#undef CAvlDeref + +#undef CAvl_Init +#undef CAvl_Insert +#undef CAvl_InsertAt +#undef CAvl_Remove +#undef CAvl_Lookup +#undef CAvl_LookupExact +#undef CAvl_GetFirstGreater +#undef CAvl_GetLastLesser +#undef CAvl_GetFirstGreaterEqual +#undef CAvl_GetLastLesserEqual +#undef CAvl_GetFirst +#undef CAvl_GetLast +#undef CAvl_GetNext +#undef CAvl_GetPrev +#undef CAvl_IsEmpty +#undef CAvl_Verify +#undef CAvl_Count +#undef CAvl_IndexOf +#undef CAvl_GetAt +#undef CAvl_AssocSum +#undef CAvl_ExclusiveAssocPrefixSum +#undef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual + +#undef CAvl_link +#undef CAvl_balance +#undef CAvl_parent +#undef CAvl_count +#undef CAvl_assoc +#undef CAvl_nulllink +#undef CAvl_nullref +#undef CAvl_compare_entries +#undef CAvl_compare_key_entry +#undef CAvl_compute_node_assoc +#undef CAvl_check_parent +#undef CAvl_verify_recurser +#undef CAvl_assert_tree +#undef CAvl_update_count_from_children +#undef CAvl_rotate +#undef CAvl_subtree_min +#undef CAvl_subtree_max +#undef CAvl_replace_subtree_fix_assoc +#undef CAvl_swap_for_remove +#undef CAvl_rebalance +#undef CAvl_child_count +#undef CAvl_MAX +#undef CAvl_OPTNEG diff --git a/app/src/main/jni/badvpn/structure/CAvl_header.h b/app/src/main/jni/badvpn/structure/CAvl_header.h new file mode 100644 index 0000000..91ea7df --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CAvl_header.h @@ -0,0 +1,141 @@ +/** + * @file CAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// CAVL_PARAM_NAME - name of this data structure +// CAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// CAVL_PARAM_FEATURE_KEYS_ARE_INDICES - (0 or 1) whether to assume the keys are entry indices +// (number of entries lesser than given entry). If yes, CAVL_PARAM_TYPE_KEY is unused. +// Requires CAVL_PARAM_FEATURE_COUNTS. +// CAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// CAVL_PARAM_FEATURE_ASSOC - define to 1 for computation of an associative operation on subtrees. +// If enabled, the following macros must be defined: CAVL_PARAM_TYPE_ASSOC, +// CAVL_PARAM_VALUE_ASSOC_ZERO, CAVL_PARAM_FUN_ASSOC_VALUE, +// CAVL_PARAM_FUN_ASSOC_OPER, CAVL_PARAM_MEMBER_ASSOC. +// CAVL_PARAM_TYPE_ENTRY - type of entry +// CAVL_PARAM_TYPE_LINK - type of entry link (usually pointer or index) +// CAVL_PARAM_TYPE_KEY - type of key (only if not CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and +// not CAVL_PARAM_FEATURE_NOKEYS) +// CAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// CAVL_PARAM_TYPE_COUNT - type of count (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_TYPE_ASSOC - type of associative operation result +// CAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (type is CAVL_PARAM_TYPE_COUNT) +// CAVL_PARAM_VALUE_NULL - value of invalid link (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_VALUE_ASSOC_ZERO - zero value for associative operation (type is CAVL_PARAM_TYPE_ASSOC). +// This must be both a left- and right-identity for the associative operation. +// CAVL_PARAM_FUN_DEREF(arg, link) - dereference a non-null link; returns pointer to CAVL_PARAM_TYPE_LINK +// CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare to entries; returns -1/0/1 +// CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 +// CAVL_PARAM_FUN_ASSOC_VALUE(arg, entry) - get value of a node for associative operation. +// The result will be cast to CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_FUN_ASSOC_OPER(arg, value1, value2) - compute the associative operation on two values. +// The type of the two values is CAVL_PARAM_TYPE_ASSOC, and the result will be cast to +// CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_MEMBER_CHILD - name of the child member in entry (type is CAVL_PARAM_TYPE_LINK[2]) +// CAVL_PARAM_MEMBER_BALANCE - name of the balance member in entry (type is any signed integer) +// CAVL_PARAM_MEMBER_PARENT - name of the parent member in entry (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_MEMBER_COUNT - name of the count member in entry (type is CAVL_PARAM_TYPE_COUNT) +// (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_MEMBER_ASSOC - name of assoc member in entry (type is CAVL_PARAM_TYPE_ASSOC) + +#ifndef BADVPN_CAVL_H +#error CAvl.h has not been included +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_COUNTS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES requires CAVL_PARAM_FEATURE_COUNTS +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && CAVL_PARAM_FEATURE_NOKEYS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and CAVL_PARAM_FEATURE_NOKEYS cannot be used together +#endif + +// types +#define CAvl CAVL_PARAM_NAME +#define CAvlEntry CAVL_PARAM_TYPE_ENTRY +#define CAvlLink CAVL_PARAM_TYPE_LINK +#define CAvlRef MERGE(CAVL_PARAM_NAME, Ref) +#define CAvlArg CAVL_PARAM_TYPE_ARG +#define CAvlKey CAVL_PARAM_TYPE_KEY +#define CAvlCount CAVL_PARAM_TYPE_COUNT +#define CAvlAssoc CAVL_PARAM_TYPE_ASSOC + +// non-object public functions +#define CAvlIsNullRef MERGE(CAvl, IsNullRef) +#define CAvlIsValidRef MERGE(CAvl, IsValidRef) +#define CAvlDeref MERGE(CAvl, Deref) + +// public functions +#define CAvl_Init MERGE(CAvl, _Init) +#define CAvl_Insert MERGE(CAvl, _Insert) +#define CAvl_InsertAt MERGE(CAvl, _InsertAt) +#define CAvl_Remove MERGE(CAvl, _Remove) +#define CAvl_Lookup MERGE(CAvl, _Lookup) +#define CAvl_LookupExact MERGE(CAvl, _LookupExact) +#define CAvl_GetFirstGreater MERGE(CAvl, _GetFirstGreater) +#define CAvl_GetLastLesser MERGE(CAvl, _GetLastLesser) +#define CAvl_GetFirstGreaterEqual MERGE(CAvl, _GetFirstGreaterEqual) +#define CAvl_GetLastLesserEqual MERGE(CAvl, _GetLastLesserEqual) +#define CAvl_GetFirst MERGE(CAvl, _GetFirst) +#define CAvl_GetLast MERGE(CAvl, _GetLast) +#define CAvl_GetNext MERGE(CAvl, _GetNext) +#define CAvl_GetPrev MERGE(CAvl, _GetPrev) +#define CAvl_IsEmpty MERGE(CAvl, _IsEmpty) +#define CAvl_Verify MERGE(CAvl, _Verify) +#define CAvl_Count MERGE(CAvl, _Count) +#define CAvl_IndexOf MERGE(CAvl, _IndexOf) +#define CAvl_GetAt MERGE(CAvl, _GetAt) +#define CAvl_AssocSum MERGE(CAvl, _AssocSum) +#define CAvl_ExclusiveAssocPrefixSum MERGE(CAvl, _ExclusiveAssocPrefixSum) +#define CAvl_FindLastExclusiveAssocPrefixSumLesserEqual MERGE(CAvl, _FindLastExclusiveAssocPrefixSumLesserEqual) + +// private stuff +#define CAvl_link(entry) ((entry).ptr->CAVL_PARAM_MEMBER_CHILD) +#define CAvl_balance(entry) ((entry).ptr->CAVL_PARAM_MEMBER_BALANCE) +#define CAvl_parent(entry) ((entry).ptr->CAVL_PARAM_MEMBER_PARENT) +#define CAvl_count(entry) ((entry).ptr->CAVL_PARAM_MEMBER_COUNT) +#define CAvl_assoc(entry) ((entry).ptr->CAVL_PARAM_MEMBER_ASSOC) +#define CAvl_nulllink MERGE(CAvl, __nulllink) +#define CAvl_nullref MERGE(CAvl, __nullref) +#define CAvl_compare_entries MERGE(CAVL_PARAM_NAME, _compare_entries) +#define CAvl_compare_key_entry MERGE(CAVL_PARAM_NAME, _compare_key_entry) +#define CAvl_compute_node_assoc MERGE(CAVL_PARAM_NAME, _compute_node_assoc) +#define CAvl_check_parent MERGE(CAVL_PARAM_NAME, _check_parent) +#define CAvl_verify_recurser MERGE(CAVL_PARAM_NAME, _verify_recurser) +#define CAvl_assert_tree MERGE(CAVL_PARAM_NAME, _assert_tree) +#define CAvl_update_count_from_children MERGE(CAVL_PARAM_NAME, _update_count_from_children) +#define CAvl_rotate MERGE(CAVL_PARAM_NAME, _rotate) +#define CAvl_subtree_min MERGE(CAVL_PARAM_NAME, _subtree_min) +#define CAvl_subtree_max MERGE(CAVL_PARAM_NAME, _subtree_max) +#define CAvl_replace_subtree_fix_assoc MERGE(CAVL_PARAM_NAME, _replace_subtree_fix_counts) +#define CAvl_swap_for_remove MERGE(CAVL_PARAM_NAME, _swap_entries) +#define CAvl_rebalance MERGE(CAVL_PARAM_NAME, _rebalance) +#define CAvl_child_count MERGE(CAvl, __child_count) +#define CAvl_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define CAvl_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) diff --git a/app/src/main/jni/badvpn/structure/CAvl_impl.h b/app/src/main/jni/badvpn/structure/CAvl_impl.h new file mode 100644 index 0000000..984bdea --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CAvl_impl.h @@ -0,0 +1,949 @@ +/** + * @file CAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +static CAvlLink CAvl_nulllink (void) +{ + return CAVL_PARAM_VALUE_NULL; +} + +static CAvlRef CAvl_nullref (void) +{ + CAvlRef n; + n.link = CAVL_PARAM_VALUE_NULL; + n.ptr = NULL; + return n; +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_compare_entries (CAvlArg arg, CAvlRef node1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, node1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#if !CAVL_PARAM_FEATURE_NOKEYS + +static int CAvl_compare_key_entry (CAvlArg arg, CAvlKey key1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#endif + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_compute_node_assoc (CAvlArg arg, CAvlRef node) +{ + CAvlAssoc sum = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); + if (CAvl_link(node)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])), sum); + } + if (CAvl_link(node)[1] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[1]))); + } + return sum; +} + +#endif + +static int CAvl_check_parent (CAvlRef p, CAvlRef c) +{ + return (p.link == CAvl_parent(c)) && (p.link == CAvl_nulllink() || c.link == CAvl_link(p)[0] || c.link == CAvl_link(p)[1]); +} + +static int CAvl_verify_recurser (CAvlArg arg, CAvlRef n) +{ + ASSERT_FORCE(CAvl_balance(n) >= -1) + ASSERT_FORCE(CAvl_balance(n) <= 1) + + int height_left = 0; + int height_right = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvlCount count_left = 0; + CAvlCount count_right = 0; +#endif + + // check left subtree + if (CAvl_link(n)[0] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[0])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[0]), n) == -1) +#endif + // recursively calculate height + height_left = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[0])); +#if CAVL_PARAM_FEATURE_COUNTS + count_left = CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])); +#endif + } + + // check right subtree + if (CAvl_link(n)[1] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[1])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[1]), n) == 1) +#endif + // recursively calculate height + height_right = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[1])); +#if CAVL_PARAM_FEATURE_COUNTS + count_right = CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])); +#endif + } + + // check balance factor + ASSERT_FORCE(CAvl_balance(n) == height_right - height_left) + +#if CAVL_PARAM_FEATURE_COUNTS + // check count + ASSERT_FORCE(CAvl_count(n) == 1 + count_left + count_right) +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + // check assoc + ASSERT_FORCE(CAvl_assoc(n) == CAvl_compute_node_assoc(arg, n)) +#endif + + return CAvl_MAX(height_left, height_right) + 1; +} + +static void CAvl_assert_tree (CAvl *o, CAvlArg arg) +{ +#ifdef CAVL_AUTO_VERIFY + CAvl_Verify(o, arg); +#endif +} + +#if CAVL_PARAM_FEATURE_COUNTS +static void CAvl_update_count_from_children (CAvlArg arg, CAvlRef n) +{ + CAvlCount left_count = CAvl_link(n)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])) : 0; + CAvlCount right_count = CAvl_link(n)[1] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])) : 0; + CAvl_count(n) = 1 + left_count + right_count; +} +#endif + +static void CAvl_rotate (CAvl *o, CAvlArg arg, CAvlRef r, uint8_t dir, CAvlRef r_parent) +{ + ASSERT(CAvl_check_parent(r_parent, r)) + CAvlRef nr = CAvlDeref(arg, CAvl_link(r)[!dir]); + + CAvl_link(r)[!dir] = CAvl_link(nr)[dir]; + if (CAvl_link(r)[!dir] != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(r)[!dir])) = r.link; + } + CAvl_link(nr)[dir] = r.link; + CAvl_parent(nr) = r_parent.link; + if (r_parent.link != CAvl_nulllink()) { + CAvl_link(r_parent)[r.link == CAvl_link(r_parent)[1]] = nr.link; + } else { + o->root = nr.link; + } + CAvl_parent(r) = nr.link; + +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_update_count_from_children(arg, r); + CAvl_update_count_from_children(arg, nr); +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(r) = CAvl_compute_node_assoc(arg, r); + CAvl_assoc(nr) = CAvl_compute_node_assoc(arg, nr); +#endif +} + +static CAvlRef CAvl_subtree_min (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[0] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[0]); + } + + return n; +} + +static CAvlRef CAvl_subtree_max (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[1] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[1]); + } + + return n; +} + +static void CAvl_replace_subtree_fix_assoc (CAvl *o, CAvlArg arg, CAvlRef dest, CAvlRef n, CAvlRef dest_parent) +{ + ASSERT(dest.link != CAvl_nulllink()) + ASSERT(CAvl_check_parent(dest_parent, dest)) + + if (dest_parent.link != CAvl_nulllink()) { + CAvl_link(dest_parent)[dest.link == CAvl_link(dest_parent)[1]] = n.link; + } else { + o->root = n.link; + } + if (n.link != CAvl_nulllink()) { + CAvl_parent(n) = CAvl_parent(dest); + } + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef c = dest_parent; c.link != CAvl_nulllink(); c = CAvlDeref(arg, CAvl_parent(c))) { +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_count(c) >= CAvl_count(dest)) + CAvl_count(c) -= CAvl_count(dest); + if (n.link != CAvl_nulllink()) { + ASSERT(CAvl_count(n) <= CAVL_PARAM_VALUE_COUNT_MAX - CAvl_count(c)) + CAvl_count(c) += CAvl_count(n); + } +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(c) = CAvl_compute_node_assoc(arg, c); +#endif + } +#endif +} + +static void CAvl_swap_for_remove (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef enode, CAvlRef node_parent, CAvlRef enode_parent) +{ + ASSERT(CAvl_check_parent(node_parent, node)) + ASSERT(CAvl_check_parent(enode_parent, enode)) + + if (enode_parent.link == node.link) { + // when the nodes are directly connected we need special handling + + uint8_t side = (enode.link == CAvl_link(node)[1]); + CAvlRef c = CAvlDeref(arg, CAvl_link(node)[!side]); + + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + + CAvl_parent(enode) = CAvl_parent(node); + if (node_parent.link != CAvl_nulllink()) { + CAvl_link(node_parent)[node.link == CAvl_link(node_parent)[1]] = enode.link; + } else { + o->root = enode.link; + } + + CAvl_link(enode)[side] = node.link; + CAvl_parent(node) = enode.link; + if ((CAvl_link(enode)[!side] = c.link) != CAvl_nulllink()) { + CAvl_parent(c) = enode.link; + } + } else { + CAvlRef temp; + + // swap parents + temp = node_parent; + CAvl_parent(node) = CAvl_parent(enode); + if (enode_parent.link != CAvl_nulllink()) { + CAvl_link(enode_parent)[enode.link == CAvl_link(enode_parent)[1]] = node.link; + } else { + o->root = node.link; + } + CAvl_parent(enode) = temp.link; + if (temp.link != CAvl_nulllink()) { + CAvl_link(temp)[node.link == CAvl_link(temp)[1]] = enode.link; + } else { + o->root = enode.link; + } + + // swap left children + temp = CAvlDeref(arg, CAvl_link(node)[0]); + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(enode)[0] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[0])) = enode.link; + } + + // swap right children + temp = CAvlDeref(arg, CAvl_link(node)[1]); + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + if ((CAvl_link(enode)[1] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[1])) = enode.link; + } + } + + // swap balance factors + int8_t b = CAvl_balance(node); + CAvl_balance(node) = CAvl_balance(enode); + CAvl_balance(enode) = b; + +#if CAVL_PARAM_FEATURE_COUNTS + // swap counts + CAvlCount c = CAvl_count(node); + CAvl_count(node) = CAvl_count(enode); + CAvl_count(enode) = c; +#endif + + // not fixing assoc values here because CAvl_replace_subtree_fix_assoc() will do it +} + +static void CAvl_rebalance (CAvl *o, CAvlArg arg, CAvlRef node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(CAvl_balance(node) >= -1 && CAvl_balance(node) <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = CAvl_MAX(deltac, CAvl_OPTNEG(CAvl_balance(node), side)) - CAvl_MAX(0, CAvl_OPTNEG(CAvl_balance(node), side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + CAvl_balance(node) -= CAvl_OPTNEG(deltac, side); + + CAvlRef child; + CAvlRef gchild; + + // perform transformations if the balance factor is wrong + if (CAvl_balance(node) == 2 || CAvl_balance(node) == -2) { + uint8_t bside; + int8_t bsidef; + if (CAvl_balance(node) == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(CAvl_link(node)[bside] != CAvl_nulllink()) + child = CAvlDeref(arg, CAvl_link(node)[bside]); + + switch (CAvl_balance(child) * bsidef) { + case 1: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 0; + CAvl_balance(child) = 0; + node = child; + delta -= 1; + break; + case 0: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 1 * bsidef; + CAvl_balance(child) = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(CAvl_link(child)[!bside] != CAvl_nulllink()) + gchild = CAvlDeref(arg, CAvl_link(child)[!bside]); + CAvl_rotate(o, arg, child, bside, node); + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = -CAvl_MAX(0, CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(child) = CAvl_MAX(0, -CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(gchild) = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (CAvl_parent(node) != CAvl_nulllink()) { + CAvlRef node_parent = CAvlDeref(arg, CAvl_parent(node)); + CAvl_rebalance(o, arg, node_parent, node.link == CAvl_link(node_parent)[1], delta); + } +} + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static CAvlCount CAvl_child_count (CAvlArg arg, CAvlRef n, int dir) +{ + return (CAvl_link(n)[dir] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[dir])) : 0); +} +#endif + +static int CAvlIsNullRef (CAvlRef node) +{ + return node.link == CAvl_nulllink(); +} + +static int CAvlIsValidRef (CAvlRef node) +{ + return node.link != CAvl_nulllink(); +} + +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link) +{ + if (link == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef n; + n.ptr = CAVL_PARAM_FUN_DEREF(arg, link); + n.link = link; + + ASSERT(n.ptr) + + return n; +} + +static void CAvl_Init (CAvl *o) +{ + o->root = CAvl_nulllink(); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref) +{ + ASSERT(node.link != CAvl_nulllink()) +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) +#endif + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = CAvl_nullref(); + } + return 1; + } + + CAvlRef c = CAvlDeref(arg, o->root); + int side; + while (1) { + int comp = CAvl_compare_entries(arg, node, c); + + if (comp == 0) { + if (out_ref) { + *out_ref = c; + } + return 0; + } + + side = (comp == 1); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(p)++; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } +#endif + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = c; + } + return 1; +} + +#else + +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(index <= CAvl_Count(o, arg)) + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + return; + } + + CAvlRef c = CAvlDeref(arg, o->root); + CAvlCount c_idx = CAvl_child_count(arg, c, 0); + int side; + while (1) { + side = (index > c_idx); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + + if (side == 0) { + c_idx -= 1 + CAvl_child_count(arg, c, 1); + } else { + c_idx += 1 + CAvl_child_count(arg, c, 0); + } + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { + CAvl_count(p)++; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + return; +} + +#endif + +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink() && CAvl_link(node)[1] != CAvl_nulllink()) { + CAvlRef max = CAvl_subtree_max(arg, CAvlDeref(arg, CAvl_link(node)[0])); + CAvl_swap_for_remove(o, arg, node, max, CAvlDeref(arg, CAvl_parent(node)), CAvlDeref(arg, CAvl_parent(max))); + } + + ASSERT(CAvl_link(node)[0] == CAvl_nulllink() || CAvl_link(node)[1] == CAvl_nulllink()) + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + CAvlRef child = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvlDeref(arg, CAvl_link(node)[0]) : CAvlDeref(arg, CAvl_link(node)[1])); + + if (paren.link != CAvl_nulllink()) { + int side = (node.link == CAvl_link(paren)[1]); + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + CAvl_rebalance(o, arg, paren, side, -1); + } else { + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + } + + CAvl_assert_tree(o, arg); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS + +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return c; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return CAvl_nullref(); + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) >= 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) < 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) <= 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) > 0); + + return c; +} + +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) > 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) <= 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) < 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) >= 0); + + return c; +} + +#endif + +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_min(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_max(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + while (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[1]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + while (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[0]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static int CAvl_IsEmpty (const CAvl *o) +{ + return o->root == CAvl_nulllink(); +} + +static void CAvl_Verify (const CAvl *o, CAvlArg arg) +{ + if (o->root != CAvl_nulllink()) { + CAvlRef root = CAvlDeref(arg, o->root); + ASSERT(CAvl_parent(root) == CAvl_nulllink()) + CAvl_verify_recurser(arg, root); + } +} + +#if CAVL_PARAM_FEATURE_COUNTS + +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg) +{ + return (o->root != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, o->root)) : 0); +} + +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlCount index = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(node)[0])) : 0); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + ASSERT(CAvl_count(paren) > CAvl_count(c)) + ASSERT(CAvl_count(paren) - CAvl_count(c) <= CAVL_PARAM_VALUE_COUNT_MAX - index) + index += CAvl_count(paren) - CAvl_count(c); + } + } + + return index; +} + +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index) +{ + if (index >= CAvl_Count(o, arg)) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + + while (1) { + ASSERT(c.link != CAvl_nulllink()) + ASSERT(index < CAvl_count(c)) + + CAvlCount left_count = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(c)[0])) : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + c = CAvlDeref(arg, CAvl_link(c)[1]); + index -= left_count + 1; + } + } +} + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAVL_PARAM_VALUE_ASSOC_ZERO; + } + CAvlRef root = CAvlDeref(arg, o->root); + return CAvl_assoc(root); +} + +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlAssoc sum = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, paren); + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_val, sum); + if (CAvl_link(paren)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(paren)[0])), sum); + } + } + } + + return sum; +} + +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user) +{ + CAvlRef result = CAvl_nullref(); + CAvlRef c = CAvlDeref(arg, o->root); + CAvlAssoc sum_offset = CAVL_PARAM_VALUE_ASSOC_ZERO; + + while (c.link != CAvl_nulllink()) { + CAvlAssoc left_sum = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(c)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + CAvlAssoc c_prefixsum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum_offset, left_sum); + + if (sum_less(user, sum, c_prefixsum)) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + result = c; + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, c); + sum_offset = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_prefixsum, c_val); + c = CAvlDeref(arg, CAvl_link(c)[1]); + } + } + + return result; +} + +#endif + +#include "CAvl_footer.h" diff --git a/app/src/main/jni/badvpn/structure/CHash.h b/app/src/main/jni/badvpn/structure/CHash.h new file mode 100644 index 0000000..45fef7a --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CHash.h @@ -0,0 +1,39 @@ +/** + * @file CHash.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CHASH_H +#define BADVPN_CHASH_H + +#include + +#include +#include +#include + +#endif diff --git a/app/src/main/jni/badvpn/structure/CHash_decl.h b/app/src/main/jni/badvpn/structure/CHash_decl.h new file mode 100644 index 0000000..d47702a --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CHash_decl.h @@ -0,0 +1,59 @@ +/** + * @file CHash_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CHash_header.h" + +typedef struct { + CHashLink *buckets; + size_t num_buckets; +} CHash; + +typedef struct { + CHashEntry *ptr; + CHashLink link; +} CHashRef; + +static CHashLink CHashNullLink (void); +static CHashRef CHashNullRef (void); +static int CHashIsNullLink (CHashLink link); +static int CHashIsNullRef (CHashRef ref); +static CHashRef CHashDerefMayNull (CHashArg arg, CHashLink link); +static CHashRef CHashDerefNonNull (CHashArg arg, CHashLink link); + +static int CHash_Init (CHash *o, size_t num_buckets); +static void CHash_Free (CHash *o); +static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing); +static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry); +static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry); +static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key); +static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry); +static int CHash_MultiplyBuckets (CHash *o, CHashArg arg, int exp); +static void CHash_Verify (const CHash *o, CHashArg arg); + +#include "CHash_footer.h" diff --git a/app/src/main/jni/badvpn/structure/CHash_footer.h b/app/src/main/jni/badvpn/structure/CHash_footer.h new file mode 100644 index 0000000..cb95daf --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CHash_footer.h @@ -0,0 +1,74 @@ +/** + * @file CHash_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// preprocessor inputs +#undef CHASH_PARAM_NAME +#undef CHASH_PARAM_ENTRY +#undef CHASH_PARAM_LINK +#undef CHASH_PARAM_KEY +#undef CHASH_PARAM_ARG +#undef CHASH_PARAM_NULL +#undef CHASH_PARAM_DEREF +#undef CHASH_PARAM_ENTRYHASH +#undef CHASH_PARAM_KEYHASH +#undef CHASH_PARAM_ENTRYHASH_IS_CHEAP +#undef CHASH_PARAM_COMPARE_ENTRIES +#undef CHASH_PARAM_COMPARE_KEY_ENTRY +#undef CHASH_PARAM_ENTRY_NEXT + +// types +#undef CHash +#undef CHashEntry +#undef CHashLink +#undef CHashRef +#undef CHashArg +#undef CHashKey + +// non-object public functions +#undef CHashNullLink +#undef CHashNullRef +#undef CHashIsNullLink +#undef CHashIsNullRef +#undef CHashDerefMayNull +#undef CHashDerefNonNull + +// public functions +#undef CHash_Init +#undef CHash_Free +#undef CHash_Insert +#undef CHash_InsertMulti +#undef CHash_Remove +#undef CHash_Lookup +#undef CHash_GetNextEqual +#undef CHash_MultiplyBuckets +#undef CHash_Verify + +// private things +#undef CHash_next +#undef CHash_assert_valid_entry diff --git a/app/src/main/jni/badvpn/structure/CHash_header.h b/app/src/main/jni/badvpn/structure/CHash_header.h new file mode 100644 index 0000000..27800ac --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CHash_header.h @@ -0,0 +1,78 @@ +/** + * @file CHash_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// CHASH_PARAM_NAME - name of this data structure +// CHASH_PARAM_ENTRY - type of entry +// CHASH_PARAM_LINK - type of entry link (usually a pointer or index to an array) +// CHASH_PARAM_KEY - type of key +// CHASH_PARAM_ARG - type of argument pass through to comparisons +// CHASH_PARAM_NULL - invalid link +// CHASH_PARAM_DEREF(arg, link) - dereference a non-null link +// CHASH_PARAM_ENTRYHASH(arg, entry) - hash function for entries; returns size_t +// CHASH_PARAM_KEYHASH(arg, key) - hash function for keys; returns size_t +// CHASH_PARAM_ENTRYHASH_IS_CHEAP - define to 1 if CHASH_PARAM_ENTRYHASH is cheap (e.g. hashes are precomputed) +// CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) - compares two entries; returns 1 for equality, 0 otherwise +// CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) - compares key and entry; returns 1 for equality, 0 otherwise +// CHASH_PARAM_ENTRY_NEXT - next member in entry + +#ifndef BADVPN_CHASH_H +#error CHash.h has not been included +#endif + +// types +#define CHash CHASH_PARAM_NAME +#define CHashEntry CHASH_PARAM_ENTRY +#define CHashLink CHASH_PARAM_LINK +#define CHashRef MERGE(CHash, Ref) +#define CHashArg CHASH_PARAM_ARG +#define CHashKey CHASH_PARAM_KEY + +// non-object public functions +#define CHashNullLink MERGE(CHash, NullLink) +#define CHashNullRef MERGE(CHash, NullRef) +#define CHashIsNullLink MERGE(CHash, IsNullLink) +#define CHashIsNullRef MERGE(CHash, IsNullRef) +#define CHashDerefMayNull MERGE(CHash, DerefMayNull) +#define CHashDerefNonNull MERGE(CHash, DerefNonNull) + +// public functions +#define CHash_Init MERGE(CHash, _Init) +#define CHash_Free MERGE(CHash, _Free) +#define CHash_Insert MERGE(CHash, _Insert) +#define CHash_InsertMulti MERGE(CHash, _InsertMulti) +#define CHash_Remove MERGE(CHash, _Remove) +#define CHash_Lookup MERGE(CHash, _Lookup) +#define CHash_GetNextEqual MERGE(CHash, _GetNextEqual) +#define CHash_MultiplyBuckets MERGE(CHash, _MultiplyBuckets) +#define CHash_Verify MERGE(CHash, _Verify) + +// private things +#define CHash_next(entry) ((entry).ptr->CHASH_PARAM_ENTRY_NEXT) +#define CHash_assert_valid_entry MERGE(CHash, _assert_valid_entry) diff --git a/app/src/main/jni/badvpn/structure/CHash_impl.h b/app/src/main/jni/badvpn/structure/CHash_impl.h new file mode 100644 index 0000000..0bded84 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/CHash_impl.h @@ -0,0 +1,312 @@ +/** + * @file CHash_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CHash_header.h" + +static void CHash_assert_valid_entry (CHashArg arg, CHashRef entry) +{ + ASSERT(entry.link != CHashNullLink()) + ASSERT(entry.ptr == CHASH_PARAM_DEREF(arg, entry.link)) +} + +static CHashLink CHashNullLink (void) +{ + return CHASH_PARAM_NULL; +} + +static CHashRef CHashNullRef (void) +{ + CHashRef entry = {NULL, CHashNullLink()}; + return entry; +} + +static int CHashIsNullLink (CHashLink link) +{ + return (link == CHashNullLink()); +} + +static int CHashIsNullRef (CHashRef ref) +{ + return CHashIsNullLink(ref.link); +} + +static CHashRef CHashDerefMayNull (CHashArg arg, CHashLink link) +{ + if (link == CHashNullLink()) { + return CHashNullRef(); + } + + CHashRef entry = {CHASH_PARAM_DEREF(arg, link), link}; + ASSERT(entry.ptr) + + return entry; +} + +static CHashRef CHashDerefNonNull (CHashArg arg, CHashLink link) +{ + ASSERT(link != CHashNullLink()) + + CHashRef entry = {CHASH_PARAM_DEREF(arg, link), link}; + ASSERT(entry.ptr) + + return entry; +} + +static int CHash_Init (CHash *o, size_t num_buckets) +{ + if (num_buckets == 0) { + num_buckets = 1; + } + + o->num_buckets = num_buckets; + + o->buckets = (CHashLink *)BAllocArray(o->num_buckets, sizeof(o->buckets[0])); + if (!o->buckets) { + return 0; + } + + for (size_t i = 0; i < o->num_buckets; i++) { + o->buckets[i] = CHashNullLink(); + } + + return 1; +} + +static void CHash_Free (CHash *o) +{ + BFree(o->buckets); +} + +static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + if (CHASH_PARAM_COMPARE_ENTRIES(arg, cur, entry)) { + if (out_existing) { + *out_existing = cur; + } + return 0; + } + link = CHash_next(cur); + } + + CHash_next(entry) = o->buckets[index]; + o->buckets[index] = entry.link; + + return 1; +} + +static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + if (CHASH_PARAM_COMPARE_ENTRIES(arg, cur, entry)) { + break; + } + prev = cur; + link = CHash_next(cur); + } + + if (link == CHashNullLink() || prev.link == CHashNullLink()) { + CHash_next(entry) = o->buckets[index]; + o->buckets[index] = entry.link; + } else { + CHash_next(entry) = link; + CHash_next(prev) = entry.link; + } +} + +static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[index]; + while (link != entry.link) { + CHashRef cur = CHashDerefNonNull(arg, link); + prev = cur; + link = CHash_next(cur); + ASSERT(link != CHashNullLink()) + } + + if (prev.link == CHashNullLink()) { + o->buckets[index] = CHash_next(entry); + } else { + CHash_next(prev) = CHash_next(entry); + } +} + +static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key) +{ + size_t hash = CHASH_PARAM_KEYHASH(arg, key); + size_t index = hash % o->num_buckets; + + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); +#if CHASH_PARAM_ENTRYHASH_IS_CHEAP + if (CHASH_PARAM_ENTRYHASH(arg, cur) == hash && CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key, cur)) { +#else + if (CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key, cur)) { +#endif + return cur; + } + link = CHash_next(cur); + } + + return CHashNullRef(); +} + +static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + CHashLink next = CHash_next(entry); + + if (next == CHashNullLink()) { + return CHashNullRef(); + } + + CHashRef next_ref = CHashDerefNonNull(arg, next); + if (!CHASH_PARAM_COMPARE_ENTRIES(arg, next_ref, entry)) { + return CHashNullRef(); + } + + return next_ref; +} + +static int CHash_MultiplyBuckets (CHash *o, CHashArg arg, int exp) +{ + ASSERT(exp > 0) + + size_t new_num_buckets = o->num_buckets; + while (exp-- > 0) { + if (new_num_buckets > SIZE_MAX / 2) { + return 0; + } + new_num_buckets *= 2; + } + + CHashLink *new_buckets = (CHashLink *)BReallocArray(o->buckets, new_num_buckets, sizeof(new_buckets[0])); + if (!new_buckets) { + return 0; + } + o->buckets = new_buckets; + + for (size_t i = o->num_buckets; i < new_num_buckets; i++) { + o->buckets[i] = CHashNullLink(); + } + + for (size_t i = 0; i < o->num_buckets; i++) { + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[i]; + + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + link = CHash_next(cur); + + size_t new_index = CHASH_PARAM_ENTRYHASH(arg, cur) % new_num_buckets; + if (new_index == i) { + prev = cur; + continue; + } + + if (CHashIsNullRef(prev)) { + o->buckets[i] = CHash_next(cur); + } else { + CHash_next(prev) = CHash_next(cur); + } + + CHash_next(cur) = o->buckets[new_index]; + o->buckets[new_index] = cur.link; + } + } + + for (size_t i = o->num_buckets; i < new_num_buckets; i++) { + CHashLink new_bucket_link = CHashNullLink(); + + CHashLink link = o->buckets[i]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + link = CHash_next(cur); + + CHash_next(cur) = new_bucket_link; + new_bucket_link = cur.link; + } + + o->buckets[i] = new_bucket_link; + } + + o->num_buckets = new_num_buckets; + + return 1; +} + +static void CHash_Verify (const CHash *o, CHashArg arg) +{ + ASSERT_FORCE(o->num_buckets > 0) + ASSERT_FORCE(o->buckets) + + for (size_t i = 0; i < o->num_buckets; i++) { + CHashRef cur = CHashDerefMayNull(arg, o->buckets[i]); + CHashRef same_first = cur; + + while (!CHashIsNullRef(cur)) { + size_t index = CHASH_PARAM_ENTRYHASH(arg, cur) % o->num_buckets; + ASSERT_FORCE(index == i) + + if (!CHASH_PARAM_COMPARE_ENTRIES(arg, cur, same_first)) { + same_first = cur; + } + + CHashRef ccur = CHashDerefNonNull(arg, o->buckets[i]); + while (ccur.link != same_first.link) { + ASSERT_FORCE(!CHASH_PARAM_COMPARE_ENTRIES(arg, ccur, cur)) + ccur = CHashDerefMayNull(arg, CHash_next(ccur)); + } + + cur = CHashDerefMayNull(arg, CHash_next(cur)); + } + } +} + +#include "CHash_footer.h" diff --git a/app/src/main/jni/badvpn/structure/ChunkBuffer2.h b/app/src/main/jni/badvpn/structure/ChunkBuffer2.h new file mode 100644 index 0000000..98073ad --- /dev/null +++ b/app/src/main/jni/badvpn/structure/ChunkBuffer2.h @@ -0,0 +1,317 @@ +/** + * @file ChunkBuffer2.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Circular packet buffer + */ + +#ifndef BADVPN_STRUCTURE_CHUNKBUFFER2_H +#define BADVPN_STRUCTURE_CHUNKBUFFER2_H + +#include +#include +#include + +#include +#include + +#ifndef NDEBUG +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) _ChunkBuffer2_assert_buffer(_buf); +#define CHUNKBUFFER2_ASSERT_IO(_buf) _ChunkBuffer2_assert_io(_buf); +#else +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) +#define CHUNKBUFFER2_ASSERT_IO(_buf) +#endif + +struct ChunkBuffer2_block { + int len; +}; + +typedef struct { + struct ChunkBuffer2_block *buffer; + int size; + int wrap; + int start; + int used; + int mtu; + uint8_t *input_dest; + int input_avail; + uint8_t *output_dest; + int output_avail; +} ChunkBuffer2; + +// calculates a buffer size needed to hold at least 'num' packets long at least 'chunk_len' +static int ChunkBuffer2_calc_blocks (int chunk_len, int num); + +// initialize +static void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu); + +// submit a packet written to the buffer +static void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len); + +// remove the first packet +static void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf); + +static int _ChunkBuffer2_end (ChunkBuffer2 *buf) +{ + if (buf->used >= buf->wrap - buf->start) { + return (buf->used - (buf->wrap - buf->start)); + } else { + return (buf->start + buf->used); + } +} + +#ifndef NDEBUG + +static void _ChunkBuffer2_assert_buffer (ChunkBuffer2 *buf) +{ + ASSERT(buf->size > 0) + ASSERT(buf->wrap > 0) + ASSERT(buf->wrap <= buf->size) + ASSERT(buf->start >= 0) + ASSERT(buf->start < buf->wrap) + ASSERT(buf->used >= 0) + ASSERT(buf->used <= buf->wrap) + ASSERT(buf->wrap == buf->size || buf->used >= buf->wrap - buf->start) + ASSERT(buf->mtu >= 0) +} + +static void _ChunkBuffer2_assert_io (ChunkBuffer2 *buf) +{ + // check input + + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } else { + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + ASSERT(buf->input_dest == (uint8_t *)&buf->buffer[end + 1]) + ASSERT(buf->input_avail == (free - 1) * sizeof(struct ChunkBuffer2_block)) + } else { + // no space + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } + } + + // check output + + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(buf->output_dest == (uint8_t *)&buf->buffer[buf->start + 1]) + ASSERT(buf->output_avail == datalen) + } else { + ASSERT(!buf->output_dest) + ASSERT(buf->output_avail == -1) + } +} + +#endif + +static void _ChunkBuffer2_update_input (ChunkBuffer2 *buf) +{ + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + buf->input_dest = NULL; + buf->input_avail = -1; + return; + } + + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + buf->input_dest = (uint8_t *)&buf->buffer[end + 1]; + buf->input_avail = (free - 1) * sizeof(struct ChunkBuffer2_block); + } else { + // no space + buf->input_dest = NULL; + buf->input_avail = -1; + } +} + +static void _ChunkBuffer2_update_output (ChunkBuffer2 *buf) +{ + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) +#ifndef NDEBUG + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) +#endif + buf->output_dest = (uint8_t *)&buf->buffer[buf->start + 1]; + buf->output_avail = datalen; + } else { + buf->output_dest = NULL; + buf->output_avail = -1; + } +} + +int ChunkBuffer2_calc_blocks (int chunk_len, int num) +{ + int chunk_data_blocks = bdivide_up(chunk_len, sizeof(struct ChunkBuffer2_block)); + + if (chunk_data_blocks > INT_MAX - 1) { + return -1; + } + int chunk_blocks = 1 + chunk_data_blocks; + + if (num > INT_MAX - 1) { + return -1; + } + int num_chunks = num + 1; + + if (chunk_blocks > INT_MAX / num_chunks) { + return -1; + } + int blocks = chunk_blocks * num_chunks; + + return blocks; +} + +void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu) +{ + ASSERT(blocks > 0) + ASSERT(mtu >= 0) + + buf->buffer = buffer; + buf->size = blocks; + buf->wrap = blocks; + buf->start = 0; + buf->used = 0; + buf->mtu = bdivide_up(mtu, sizeof(struct ChunkBuffer2_block)); + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + _ChunkBuffer2_update_input(buf); + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len) +{ + ASSERT(buf->input_dest) + ASSERT(len >= 0) + ASSERT(len <= buf->input_avail) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + int end = _ChunkBuffer2_end(buf); + int blocklen = bdivide_up(len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->size - end - 1) + ASSERT(buf->used < buf->wrap - buf->start || blocklen <= buf->start - end - 1) + + buf->buffer[end].len = len; + buf->used += 1 + blocklen; + + if (buf->used <= buf->wrap - buf->start && buf->mtu > buf->size - (end + 1 + blocklen) - 1) { + buf->wrap = end + 1 + blocklen; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + _ChunkBuffer2_update_input(buf); + + // update output + if (buf->used == 1 + blocklen) { + _ChunkBuffer2_update_output(buf); + } + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf) +{ + ASSERT(buf->output_dest) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + ASSERT(1 <= buf->wrap - buf->start) + ASSERT(1 <= buf->used) + + int blocklen = bdivide_up(buf->buffer[buf->start].len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(blocklen <= buf->used - 1) + + int data_wrapped = (buf->used >= buf->wrap - buf->start); + + buf->start += 1 + blocklen; + buf->used -= 1 + blocklen; + if (buf->start == buf->wrap) { + buf->start = 0; + buf->wrap = buf->size; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + if (data_wrapped) { + _ChunkBuffer2_update_input(buf); + } + + // update output + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/IndexedList.h b/app/src/main/jni/badvpn/structure/IndexedList.h new file mode 100644 index 0000000..ca611e9 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/IndexedList.h @@ -0,0 +1,225 @@ +/** + * @file IndexedList.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A data structure similar to a list, but with efficient index-based access. + */ + +#ifndef BADVPN_INDEXEDLIST_H +#define BADVPN_INDEXEDLIST_H + +#include +#include + +#include +#include +#include + +typedef struct IndexedList_s IndexedList; +typedef struct IndexedListNode_s IndexedListNode; + +typedef IndexedListNode *IndexedList__tree_link; + +#include "IndexedList_tree.h" +#include + +struct IndexedList_s { + IndexedList__Tree tree; +}; + +struct IndexedListNode_s { + IndexedListNode *tree_child[2]; + IndexedListNode *tree_parent; + int8_t tree_balance; + uint64_t tree_count; +}; + +/** + * Initializes the indexed list. + * + * @param o uninitialized list object to initialize + */ +static void IndexedList_Init (IndexedList *o); + +/** + * Inserts a node into the indexed list. + * + * @param o indexed list to insert into + * @param node uninitialized node to insert + * @param index index to insert at (starting with zero). Any existing elements + * at or after this index will be shifted forward, i.e. their + * indices will be incremented by one. Must be <=count. + */ +static void IndexedList_InsertAt (IndexedList *o, IndexedListNode *node, uint64_t index); + +/** + * Removes a nove from the indexed list. + * + * @param o indexed list to remove from + * @param node node in the list to remove + */ +static void IndexedList_Remove (IndexedList *o, IndexedListNode *node); + +/** + * Returns the number of nodes in the indexed list. + * + * @param o indexed list + * @return number of nodes + */ +static uint64_t IndexedList_Count (IndexedList *o); + +/** + * Returns the index of a node in the indexed list. + * + * @param o indexed list + * @param node node in the list to get index of + * @return index of the node + */ +static uint64_t IndexedList_IndexOf (IndexedList *o, IndexedListNode *node); + +/** + * Returns the node at the specified index in the indexed list. + * + * @param o indexed list + * @param index index of the node to return. Must be < count. + * @return node at the specified index + */ +static IndexedListNode * IndexedList_GetAt (IndexedList *o, uint64_t index); + +/** + * Returns the first node, or NULL if the list is empty. + * + * @param o indexed list + * @return first node, or NULL + */ +static IndexedListNode * IndexedList_GetFirst (IndexedList *o); + +/** + * Returns the last node, or NULL if the list is empty. + * + * @param o indexed list + * @return last node, or NULL + */ +static IndexedListNode * IndexedList_GetLast (IndexedList *o); + +/** + * Returns the next node of a given node, or NULL this is the last node. + * + * @param o indexed list + * @param node existing node + * @return next node, or NULL + */ +static IndexedListNode * IndexedList_GetNext (IndexedList *o, IndexedListNode *node); + +/** + * Returns the previous node of a given node, or NULL this is the first node. + * + * @param o indexed list + * @param node existing node + * @return previous node, or NULL + */ +static IndexedListNode * IndexedList_GetPrev (IndexedList *o, IndexedListNode *node); + +#include "IndexedList_tree.h" +#include + +static IndexedListNode * IndexedList__deref (IndexedList__TreeRef ref) +{ + return ref.link; +} + +static void IndexedList_Init (IndexedList *o) +{ + IndexedList__Tree_Init(&o->tree); +} + +static void IndexedList_InsertAt (IndexedList *o, IndexedListNode *node, uint64_t index) +{ + ASSERT(index <= IndexedList__Tree_Count(&o->tree, 0)) + ASSERT(IndexedList__Tree_Count(&o->tree, 0) < UINT64_MAX - 1) + + uint64_t orig_count = IndexedList__Tree_Count(&o->tree, 0); + B_USE(orig_count) + + IndexedList__Tree_InsertAt(&o->tree, 0, IndexedList__TreeDeref(0, node), index); + + ASSERT(IndexedList__Tree_IndexOf(&o->tree, 0, IndexedList__TreeDeref(0, node)) == index) + ASSERT(IndexedList__Tree_Count(&o->tree, 0) == orig_count + 1) +} + +static void IndexedList_Remove (IndexedList *o, IndexedListNode *node) +{ + IndexedList__Tree_Remove(&o->tree, 0, IndexedList__TreeDeref(0, node)); +} + +static uint64_t IndexedList_Count (IndexedList *o) +{ + return IndexedList__Tree_Count(&o->tree, 0); +} + +static uint64_t IndexedList_IndexOf (IndexedList *o, IndexedListNode *node) +{ + return IndexedList__Tree_IndexOf(&o->tree, 0, IndexedList__TreeDeref(0, node)); +} + +static IndexedListNode * IndexedList_GetAt (IndexedList *o, uint64_t index) +{ + ASSERT(index < IndexedList__Tree_Count(&o->tree, 0)) + + IndexedList__TreeRef ref = IndexedList__Tree_GetAt(&o->tree, 0, index); + ASSERT(!IndexedList__TreeIsNullRef(ref)) + + return ref.ptr; +} + +static IndexedListNode * IndexedList_GetFirst (IndexedList *o) +{ + return IndexedList__deref(IndexedList__Tree_GetFirst(&o->tree, 0)); +} + +static IndexedListNode * IndexedList_GetLast (IndexedList *o) +{ + return IndexedList__deref(IndexedList__Tree_GetLast(&o->tree, 0)); +} + +static IndexedListNode * IndexedList_GetNext (IndexedList *o, IndexedListNode *node) +{ + ASSERT(node) + + return IndexedList__deref(IndexedList__Tree_GetNext(&o->tree, 0, IndexedList__TreeDeref(0, node))); +} + +static IndexedListNode * IndexedList_GetPrev (IndexedList *o, IndexedListNode *node) +{ + ASSERT(node) + + return IndexedList__deref(IndexedList__Tree_GetPrev(&o->tree, 0, IndexedList__TreeDeref(0, node))); +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/IndexedList_tree.h b/app/src/main/jni/badvpn/structure/IndexedList_tree.h new file mode 100644 index 0000000..130f00f --- /dev/null +++ b/app/src/main/jni/badvpn/structure/IndexedList_tree.h @@ -0,0 +1,15 @@ +#define CAVL_PARAM_NAME IndexedList__Tree +#define CAVL_PARAM_FEATURE_COUNTS 1 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 1 +#define CAVL_PARAM_FEATURE_NOKEYS 0 +#define CAVL_PARAM_TYPE_ENTRY IndexedListNode +#define CAVL_PARAM_TYPE_LINK IndexedList__tree_link +#define CAVL_PARAM_TYPE_ARG int +#define CAVL_PARAM_TYPE_COUNT uint64_t +#define CAVL_PARAM_VALUE_COUNT_MAX UINT64_MAX +#define CAVL_PARAM_VALUE_NULL ((IndexedList__tree_link)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent +#define CAVL_PARAM_MEMBER_COUNT tree_count diff --git a/app/src/main/jni/badvpn/structure/LinkedList0.h b/app/src/main/jni/badvpn/structure/LinkedList0.h new file mode 100644 index 0000000..feef4d7 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/LinkedList0.h @@ -0,0 +1,202 @@ +/** + * @file LinkedList0.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Very simple doubly linked list, with only a 'first' pointer an no 'last' + * pointer. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST0_H +#define BADVPN_STRUCTURE_LINKEDLIST0_H + +#include + +#include + +/** + * Linked list node. + */ +typedef struct LinkedList0Node_t +{ + struct LinkedList0Node_t *p; + struct LinkedList0Node_t *n; +} LinkedList0Node; + +/** + * Simple doubly linked list. + */ +typedef struct +{ + LinkedList0Node *first; +} LinkedList0; + +/** + * Initializes the linked list. + * + * @param list list to initialize + */ +static void LinkedList0_Init (LinkedList0 *list); + +/** + * Determines if the list is empty. + * + * @param list the list + * @return 1 if empty, 0 if not + */ +static int LinkedList0_IsEmpty (LinkedList0 *list); + +/** + * Returns the first node of the list. + * + * @param list the list + * @return first node of the list, or NULL if the list is empty + */ +static LinkedList0Node * LinkedList0_GetFirst (LinkedList0 *list); + +/** + * Inserts a node to the beginning of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList0_Prepend (LinkedList0 *list, LinkedList0Node *node); + +/** + * Inserts a node before a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert before + */ +static void LinkedList0_InsertBefore (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target); + +/** + * Inserts a node after a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert after + */ +static void LinkedList0_InsertAfter (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target); + +/** + * Removes a node from the list. + * + * @param list the list + * @param node node to remove + */ +static void LinkedList0_Remove (LinkedList0 *list, LinkedList0Node *node); + +/** + * Returns the next node of a given node. + * + * @param node reference node + * @return next node, or NULL if none + */ +static LinkedList0Node * LinkedList0Node_Next (LinkedList0Node *node); + +/** + * Returns the previous node of a given node. + * + * @param node reference node + * @return previous node, or NULL if none + */ +static LinkedList0Node * LinkedList0Node_Prev (LinkedList0Node *node); + +void LinkedList0_Init (LinkedList0 *list) +{ + list->first = NULL; +} + +int LinkedList0_IsEmpty (LinkedList0 *list) +{ + return (!list->first); +} + +LinkedList0Node * LinkedList0_GetFirst (LinkedList0 *list) +{ + return (list->first); +} + +void LinkedList0_Prepend (LinkedList0 *list, LinkedList0Node *node) +{ + node->p = NULL; + node->n = list->first; + if (list->first) { + list->first->p = node; + } + list->first = node; +} + +void LinkedList0_InsertBefore (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target) +{ + node->p = target->p; + node->n = target; + if (target->p) { + target->p->n = node; + } else { + list->first = node; + } + target->p = node; +} + +void LinkedList0_InsertAfter (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target) +{ + node->p = target; + node->n = target->n; + if (target->n) { + target->n->p = node; + } + target->n = node; +} + +void LinkedList0_Remove (LinkedList0 *list, LinkedList0Node *node) +{ + // remove from list + if (node->p) { + node->p->n = node->n; + } else { + list->first = node->n; + } + if (node->n) { + node->n->p = node->p; + } +} + +LinkedList0Node * LinkedList0Node_Next (LinkedList0Node *node) +{ + return node->n; +} + +LinkedList0Node * LinkedList0Node_Prev (LinkedList0Node *node) +{ + return node->p; +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/LinkedList1.h b/app/src/main/jni/badvpn/structure/LinkedList1.h new file mode 100644 index 0000000..bdb7161 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/LinkedList1.h @@ -0,0 +1,275 @@ +/** + * @file LinkedList1.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Simple doubly linked list. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST1_H +#define BADVPN_STRUCTURE_LINKEDLIST1_H + +#include + +#include + +/** + * Linked list node. + */ +typedef struct LinkedList1Node_t +{ + struct LinkedList1Node_t *p; + struct LinkedList1Node_t *n; +} LinkedList1Node; + +/** + * Simple doubly linked list. + */ +typedef struct +{ + LinkedList1Node *first; + LinkedList1Node *last; +} LinkedList1; + +/** + * Initializes the linked list. + * + * @param list list to initialize + */ +static void LinkedList1_Init (LinkedList1 *list); + +/** + * Determines if the list is empty. + * + * @param list the list + * @return 1 if empty, 0 if not + */ +static int LinkedList1_IsEmpty (LinkedList1 *list); + +/** + * Returns the first node of the list. + * + * @param list the list + * @return first node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list); + +/** + * Returns the last node of the list. + * + * @param list the list + * @return last node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list); + +/** + * Inserts a node to the beginning of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node to the end of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node before a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert before + */ +static void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Inserts a node after a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert after + */ +static void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Removes a node from the list. + * + * @param list the list + * @param node node to remove + */ +static void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts the nodes in the list \a ins_list into this list, after the node \a target. + * If \a target is NULL, the nodes are inserted to the beginning. + * Note that because the first and last node in \a ins_list are modified + * (unless the list is empty), \a ins_list is invalidated and must no longer + * be used to access the inserted nodes. + */ +static void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target); + +/** + * Returns the next node of a given node. + * + * @param node reference node + * @return next node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node); + +/** + * Returns the previous node of a given node. + * + * @param node reference node + * @return previous node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node); + +void LinkedList1_Init (LinkedList1 *list) +{ + list->first = NULL; + list->last = NULL; +} + +int LinkedList1_IsEmpty (LinkedList1 *list) +{ + return (!list->first); +} + +LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list) +{ + return (list->first); +} + +LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list) +{ + return (list->last); +} + +void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = NULL; + node->n = list->first; + if (list->first) { + list->first->p = node; + } else { + list->last = node; + } + list->first = node; +} + +void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = list->last; + node->n = NULL; + if (list->last) { + list->last->n = node; + } else { + list->first = node; + } + list->last = node; +} + +void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target->p; + node->n = target; + if (target->p) { + target->p->n = node; + } else { + list->first = node; + } + target->p = node; +} + +void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target; + node->n = target->n; + if (target->n) { + target->n->p = node; + } else { + list->last = node; + } + target->n = node; +} + +void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node) +{ + // remove from list + if (node->p) { + node->p->n = node->n; + } else { + list->first = node->n; + } + if (node->n) { + node->n->p = node->p; + } else { + list->last = node->p; + } +} + +void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target) +{ + if (!ins_list.first) { + return; + } + + LinkedList1Node *t_next = (target ? target->n : list->first); + + ins_list.first->p = target; + ins_list.last->n = t_next; + + if (target) { + target->n = ins_list.first; + } else { + list->first = ins_list.first; + } + + if (t_next) { + t_next->p = ins_list.last; + } else { + list->last = ins_list.last; + } +} + +LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node) +{ + return node->n; +} + +LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node) +{ + return node->p; +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/LinkedList3.h b/app/src/main/jni/badvpn/structure/LinkedList3.h new file mode 100644 index 0000000..8f54cdb --- /dev/null +++ b/app/src/main/jni/badvpn/structure/LinkedList3.h @@ -0,0 +1,362 @@ +/** + * @file LinkedList3.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Doubly linked list that support multiple iterations and removing + * aritrary elements during iteration, without a central object to + * represent the list. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST3_H +#define BADVPN_STRUCTURE_LINKEDLIST3_H + +#include +#include + +#include + +struct _LinkedList3Iterator; + +/** + * Linked list node. + */ +typedef struct _LinkedList3Node { + struct _LinkedList3Node *p; + struct _LinkedList3Node *n; + struct _LinkedList3Iterator *it; +} LinkedList3Node; + +/** + * Linked list iterator. + */ +typedef struct _LinkedList3Iterator { + int8_t dir; + struct _LinkedList3Node *e; + struct _LinkedList3Iterator *pi; + struct _LinkedList3Iterator *ni; +} LinkedList3Iterator; + +/** + * Initializes a list node to form a new list consisting of a + * single node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + */ +static void LinkedList3Node_InitLonely (LinkedList3Node *node); + +/** + * Initializes a list node to go after an existing node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + * @param ref existing list node + */ +static void LinkedList3Node_InitAfter (LinkedList3Node *node, LinkedList3Node *ref); + +/** + * Initializes a list node to go before an existing node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + * @param ref existing list node + */ +static void LinkedList3Node_InitBefore (LinkedList3Node *node, LinkedList3Node *ref); + +/** + * Frees a list node, removing it a list (if there were other nodes + * in the list). + * + * @param node list node to free + */ +static void LinkedList3Node_Free (LinkedList3Node *node); + +/** + * Determines if a list node is a single node in a list. + * + * @param node list node + * @return 1 if the node ia a single node, 0 if not + */ +static int LinkedList3Node_IsLonely (LinkedList3Node *node); + +/** + * Returnes the node preceding this node (if there is one), + * the node following this node (if there is one), or NULL, + * respectively. + * + * @param node list node + * @return neighbour node or NULL if none + */ +static LinkedList3Node * LinkedList3Node_PrevOrNext (LinkedList3Node *node); + +/** + * Returnes the node following this node (if there is one), + * the node preceding this node (if there is one), or NULL, + * respectively. + * + * @param node list node + * @return neighbour node or NULL if none + */ +static LinkedList3Node * LinkedList3Node_NextOrPrev (LinkedList3Node *node); + +/** + * Returns the node preceding this node, or NULL if there is none. + * + * @param node list node + * @return left neighbour, or NULL if none + */ +static LinkedList3Node * LinkedList3Node_Prev (LinkedList3Node *node); + +/** + * Returns the node following this node, or NULL if there is none. + * + * @param node list node + * @return right neighbour, or NULL if none + */ +static LinkedList3Node * LinkedList3Node_Next (LinkedList3Node *node); + +/** + * Returns the first node in the list which this node is part of. + * It is found by iterating the list from this node to the beginning. + * + * @param node list node + * @return first node in the list + */ +static LinkedList3Node * LinkedList3Node_First (LinkedList3Node *node); + +/** + * Returns the last node in the list which this node is part of. + * It is found by iterating the list from this node to the end. + * + * @param node list node + * @return last node in the list + */ +static LinkedList3Node * LinkedList3Node_Last (LinkedList3Node *node); + +/** + * Initializes a linked list iterator. + * The iterator structure must remain available until either of these occurs: + * - the list is no longer needed, or + * - the iterator is freed with {@link LinkedList3Iterator_Free}, or + * - the iterator reaches the end of iteration. + * + * @param it uninitialized iterator to initialize + * @param e initial position of the iterator. NULL for end of iteration. + * @param dir direction of iteration. Must be 1 (forward) or -1 (backward). + */ +static void LinkedList3Iterator_Init (LinkedList3Iterator *it, LinkedList3Node *e, int dir); + +/** + * Frees a linked list iterator. + * + * @param it iterator to free + */ +static void LinkedList3Iterator_Free (LinkedList3Iterator *it); + +/** + * Moves the iterator one node forward or backward (depending on its direction), or, + * if it's at the last or first node (depending on the direction), it reaches + * the end of iteration, or, if it's at the end of iteration, it remains there. + * Returns the the previous position. + * + * @param it the iterator + * @return node on the position of iterator before it was (possibly) moved, or NULL + * if it was at the end of iteration + */ +static LinkedList3Node * LinkedList3Iterator_Next (LinkedList3Iterator *it); + +void LinkedList3Node_InitLonely (LinkedList3Node *node) +{ + node->p = NULL; + node->n = NULL; + node->it = NULL; +} + +void LinkedList3Node_InitAfter (LinkedList3Node *node, LinkedList3Node *ref) +{ + ASSERT(ref) + + node->p = ref; + node->n = ref->n; + ref->n = node; + if (node->n) { + node->n->p = node; + } + node->it = NULL; +} + +void LinkedList3Node_InitBefore (LinkedList3Node *node, LinkedList3Node *ref) +{ + ASSERT(ref) + + node->n = ref; + node->p = ref->p; + ref->p = node; + if (node->p) { + node->p->n = node; + } + node->it = NULL; +} + +void LinkedList3Node_Free (LinkedList3Node *node) +{ + // jump iterators + while (node->it) { + LinkedList3Iterator_Next(node->it); + } + + if (node->p) { + node->p->n = node->n; + } + if (node->n) { + node->n->p = node->p; + } +} + +int LinkedList3Node_IsLonely (LinkedList3Node *node) +{ + return (!node->p && !node->n); +} + +LinkedList3Node * LinkedList3Node_PrevOrNext (LinkedList3Node *node) +{ + if (node->p) { + return node->p; + } + if (node->n) { + return node->n; + } + return NULL; +} + +LinkedList3Node * LinkedList3Node_NextOrPrev (LinkedList3Node *node) +{ + if (node->n) { + return node->n; + } + if (node->p) { + return node->p; + } + return NULL; +} + +LinkedList3Node * LinkedList3Node_Prev (LinkedList3Node *node) +{ + return node->p; +} + +LinkedList3Node * LinkedList3Node_Next (LinkedList3Node *node) +{ + return node->n; +} + +LinkedList3Node * LinkedList3Node_First (LinkedList3Node *node) +{ + while (node->p) { + node = node->p; + } + + return node; +} + +LinkedList3Node * LinkedList3Node_Last (LinkedList3Node *node) +{ + while (node->n) { + node = node->n; + } + + return node; +} + +void LinkedList3Iterator_Init (LinkedList3Iterator *it, LinkedList3Node *e, int dir) +{ + ASSERT(dir == -1 || dir == 1) + + it->dir = dir; + it->e = e; + + if (e) { + // link into node's iterator list + it->pi = NULL; + it->ni = e->it; + if (e->it) { + e->it->pi = it; + } + e->it = it; + } +} + +void LinkedList3Iterator_Free (LinkedList3Iterator *it) +{ + if (it->e) { + // remove from node's iterator list + if (it->ni) { + it->ni->pi = it->pi; + } + if (it->pi) { + it->pi->ni = it->ni; + } else { + it->e->it = it->ni; + } + } +} + +LinkedList3Node * LinkedList3Iterator_Next (LinkedList3Iterator *it) +{ + // remember original entry + LinkedList3Node *orig = it->e; + + // jump to next entry + if (it->e) { + // get next entry + LinkedList3Node *next = NULL; // to remove warning + switch (it->dir) { + case 1: + next = it->e->n; + break; + case -1: + next = it->e->p; + break; + default: + ASSERT(0); + } + // destroy interator + LinkedList3Iterator_Free(it); + // re-initialize at next entry + LinkedList3Iterator_Init(it, next, it->dir); + } + + // return original entry + return orig; +} + +#endif diff --git a/app/src/main/jni/badvpn/structure/SAvl.h b/app/src/main/jni/badvpn/structure/SAvl.h new file mode 100644 index 0000000..6ea1399 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl.h @@ -0,0 +1,40 @@ +/** + * @file SAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SAVL_H +#define BADVPN_SAVL_H + +#include +#include + +#include +#include +#include + +#endif diff --git a/app/src/main/jni/badvpn/structure/SAvl_decl.h b/app/src/main/jni/badvpn/structure/SAvl_decl.h new file mode 100644 index 0000000..8a13e49 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl_decl.h @@ -0,0 +1,73 @@ +/** + * @file SAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +typedef SAvlEntry *SAvl__TreeLink; + +#include "SAvl_tree.h" +#include + +typedef struct { + SAvl__Tree tree; +} SAvl; + +typedef struct { + SAvlEntry *child[2]; + SAvlEntry *parent; + int8_t balance; +#if SAVL_PARAM_FEATURE_COUNTS + SAvlCount count; +#endif +} SAvlNode; + +static void SAvl_Init (SAvl *o); +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing); +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry); +#if !SAVL_PARAM_FEATURE_NOKEYS +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +#endif +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static int SAvl_IsEmpty (const SAvl *o); +static void SAvl_Verify (const SAvl *o, SAvlArg arg); +#if SAVL_PARAM_FEATURE_COUNTS +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg); +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index); +#endif + +#include "SAvl_footer.h" diff --git a/app/src/main/jni/badvpn/structure/SAvl_footer.h b/app/src/main/jni/badvpn/structure/SAvl_footer.h new file mode 100644 index 0000000..ad90e40 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl_footer.h @@ -0,0 +1,89 @@ +/** + * @file SAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SAVL_PARAM_NAME +#undef SAVL_PARAM_FEATURE_COUNTS +#undef SAVL_PARAM_FEATURE_NOKEYS +#undef SAVL_PARAM_TYPE_ENTRY +#undef SAVL_PARAM_TYPE_KEY +#undef SAVL_PARAM_TYPE_ARG +#undef SAVL_PARAM_TYPE_COUNT +#undef SAVL_PARAM_VALUE_COUNT_MAX +#undef SAVL_PARAM_FUN_COMPARE_ENTRIES +#undef SAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef SAVL_PARAM_MEMBER_NODE + +#undef SAvl +#undef SAvlEntry +#undef SAvlArg +#undef SAvlKey +#undef SAvlCount +#undef SAvlNode + +#undef SAvl_Init +#undef SAvl_Insert +#undef SAvl_Remove +#undef SAvl_Lookup +#undef SAvl_LookupExact +#undef SAvl_GetFirstGreater +#undef SAvl_GetLastLesser +#undef SAvl_GetFirstGreaterEqual +#undef SAvl_GetLastLesserEqual +#undef SAvl_GetFirst +#undef SAvl_GetLast +#undef SAvl_GetNext +#undef SAvl_GetPrev +#undef SAvl_IsEmpty +#undef SAvl_Verify +#undef SAvl_Count +#undef SAvl_IndexOf +#undef SAvl_GetAt + +#undef SAvl__Tree +#undef SAvl__TreeRef +#undef SAvl__TreeLink +#undef SAvl__TreeDeref +#undef SAvl__Tree_Init +#undef SAvl__Tree_Insert +#undef SAvl__Tree_Remove +#undef SAvl__Tree_Lookup +#undef SAvl__Tree_LookupExact +#undef SAvl__Tree_GetFirstGreater +#undef SAvl__Tree_GetLastLesser +#undef SAvl__Tree_GetFirstGreaterEqual +#undef SAvl__Tree_GetLastLesserEqual +#undef SAvl__Tree_GetFirst +#undef SAvl__Tree_GetLast +#undef SAvl__Tree_GetNext +#undef SAvl__Tree_GetPrev +#undef SAvl__Tree_IsEmpty +#undef SAvl__Tree_Verify +#undef SAvl__Tree_Count +#undef SAvl__Tree_IndexOf +#undef SAvl__Tree_GetAt diff --git a/app/src/main/jni/badvpn/structure/SAvl_header.h b/app/src/main/jni/badvpn/structure/SAvl_header.h new file mode 100644 index 0000000..5d7d9b1 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl_header.h @@ -0,0 +1,93 @@ +/** + * @file SAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs. All types must be typedef names unless stated otherwise. +// SAVL_PARAM_NAME - name of this data structure +// SAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// SAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// SAVL_PARAM_TYPE_ENTRY - type of entry. This can also be a struct (e.g.: struct Foo). +// SAVL_PARAM_TYPE_KEY - type of key (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// SAVL_PARAM_TYPE_COUNT - type of count (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (of type SAVL_PARAM_TYPE_COUNT) (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare two entries; returns -1/0/1 +// SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_MEMBER_NODE - node member in entry + +// types +#define SAvl SAVL_PARAM_NAME +#define SAvlEntry SAVL_PARAM_TYPE_ENTRY +#define SAvlArg SAVL_PARAM_TYPE_ARG +#define SAvlKey SAVL_PARAM_TYPE_KEY +#define SAvlCount SAVL_PARAM_TYPE_COUNT +#define SAvlNode MERGE(SAvl, Node) + +// public functions +#define SAvl_Init MERGE(SAvl, _Init) +#define SAvl_Insert MERGE(SAvl, _Insert) +#define SAvl_Remove MERGE(SAvl, _Remove) +#define SAvl_Lookup MERGE(SAvl, _Lookup) +#define SAvl_LookupExact MERGE(SAvl, _LookupExact) +#define SAvl_GetFirstGreater MERGE(SAvl, _GetFirstGreater) +#define SAvl_GetLastLesser MERGE(SAvl, _GetLastLesser) +#define SAvl_GetFirstGreaterEqual MERGE(SAvl, _GetFirstGreaterEqual) +#define SAvl_GetLastLesserEqual MERGE(SAvl, _GetLastLesserEqual) +#define SAvl_GetFirst MERGE(SAvl, _GetFirst) +#define SAvl_GetLast MERGE(SAvl, _GetLast) +#define SAvl_GetNext MERGE(SAvl, _GetNext) +#define SAvl_GetPrev MERGE(SAvl, _GetPrev) +#define SAvl_IsEmpty MERGE(SAvl, _IsEmpty) +#define SAvl_Verify MERGE(SAvl, _Verify) +#define SAvl_Count MERGE(SAvl, _Count) +#define SAvl_IndexOf MERGE(SAvl, _IndexOf) +#define SAvl_GetAt MERGE(SAvl, _GetAt) + +// internal stuff +#define SAvl__Tree MERGE(SAvl, __Tree) +#define SAvl__TreeRef MERGE(SAvl, __TreeRef) +#define SAvl__TreeLink MERGE(SAvl, __TreeLink) +#define SAvl__TreeDeref MERGE(SAvl, __TreeDeref) +#define SAvl__Tree_Init MERGE(SAvl, __Tree_Init) +#define SAvl__Tree_Insert MERGE(SAvl, __Tree_Insert) +#define SAvl__Tree_Remove MERGE(SAvl, __Tree_Remove) +#define SAvl__Tree_Lookup MERGE(SAvl, __Tree_Lookup) +#define SAvl__Tree_LookupExact MERGE(SAvl, __Tree_LookupExact) +#define SAvl__Tree_GetFirstGreater MERGE(SAvl, __Tree_GetFirstGreater) +#define SAvl__Tree_GetLastLesser MERGE(SAvl, __Tree_GetLastLesser) +#define SAvl__Tree_GetFirstGreaterEqual MERGE(SAvl, __Tree_GetFirstGreaterEqual) +#define SAvl__Tree_GetLastLesserEqual MERGE(SAvl, __Tree_GetLastLesserEqual) +#define SAvl__Tree_GetFirst MERGE(SAvl, __Tree_GetFirst) +#define SAvl__Tree_GetLast MERGE(SAvl, __Tree_GetLast) +#define SAvl__Tree_GetNext MERGE(SAvl, __Tree_GetNext) +#define SAvl__Tree_GetPrev MERGE(SAvl, __Tree_GetPrev) +#define SAvl__Tree_IsEmpty MERGE(SAvl, __Tree_IsEmpty) +#define SAvl__Tree_Verify MERGE(SAvl, __Tree_Verify) +#define SAvl__Tree_Count MERGE(SAvl, __Tree_Count) +#define SAvl__Tree_IndexOf MERGE(SAvl, __Tree_IndexOf) +#define SAvl__Tree_GetAt MERGE(SAvl, __Tree_GetAt) diff --git a/app/src/main/jni/badvpn/structure/SAvl_impl.h b/app/src/main/jni/badvpn/structure/SAvl_impl.h new file mode 100644 index 0000000..0d3973a --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl_impl.h @@ -0,0 +1,164 @@ +/** + * @file SAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +#include "SAvl_tree.h" +#include + +static void SAvl_Init (SAvl *o) +{ + SAvl__Tree_Init(&o->tree); +} + +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing) +{ + ASSERT(entry) +#if SAVL_PARAM_FEATURE_COUNTS + ASSERT(SAvl_Count(o, arg) < SAVL_PARAM_VALUE_COUNT_MAX) +#endif + + SAvl__TreeRef out_ref; + int res = SAvl__Tree_Insert(&o->tree, arg, SAvl__TreeDeref(arg, entry), &out_ref); + + if (out_existing) { + *out_existing = out_ref.link; + } + + return res; +} + +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__Tree_Remove(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +#if !SAVL_PARAM_FEATURE_NOKEYS + +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_Lookup(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_LookupExact(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreater(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesser(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreaterEqual(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesserEqual(&o->tree, arg, key); + return ref.link; +} + +#endif + +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirst(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLast(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetNext(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetPrev(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static int SAvl_IsEmpty (const SAvl *o) +{ + return SAvl__Tree_IsEmpty(&o->tree); +} + +static void SAvl_Verify (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Verify(&o->tree, arg); +} + +#if SAVL_PARAM_FEATURE_COUNTS + +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Count(&o->tree, arg); +} + +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + return SAvl__Tree_IndexOf(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index) +{ + SAvl__TreeRef ref = SAvl__Tree_GetAt(&o->tree, arg, index); + return ref.link; +} + +#endif + +#include "SAvl_footer.h" diff --git a/app/src/main/jni/badvpn/structure/SAvl_tree.h b/app/src/main/jni/badvpn/structure/SAvl_tree.h new file mode 100644 index 0000000..5e5b18b --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SAvl_tree.h @@ -0,0 +1,18 @@ +#define CAVL_PARAM_NAME SAvl__Tree +#define CAVL_PARAM_FEATURE_COUNTS SAVL_PARAM_FEATURE_COUNTS +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS SAVL_PARAM_FEATURE_NOKEYS +#define CAVL_PARAM_TYPE_ENTRY SAvlEntry +#define CAVL_PARAM_TYPE_LINK SAvl__TreeLink +#define CAVL_PARAM_TYPE_KEY SAvlKey +#define CAVL_PARAM_TYPE_ARG SAvlArg +#define CAVL_PARAM_TYPE_COUNT SAvlCount +#define CAVL_PARAM_VALUE_COUNT_MAX SAVL_PARAM_VALUE_COUNT_MAX +#define CAVL_PARAM_VALUE_NULL ((SAvl__TreeLink)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) SAVL_PARAM_FUN_COMPARE_ENTRIES((arg), (entry1).link, (entry2).link) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) SAVL_PARAM_FUN_COMPARE_KEY_ENTRY((arg), (key1), (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD SAVL_PARAM_MEMBER_NODE . child +#define CAVL_PARAM_MEMBER_BALANCE SAVL_PARAM_MEMBER_NODE . balance +#define CAVL_PARAM_MEMBER_PARENT SAVL_PARAM_MEMBER_NODE . parent +#define CAVL_PARAM_MEMBER_COUNT SAVL_PARAM_MEMBER_NODE . count diff --git a/app/src/main/jni/badvpn/structure/SLinkedList.h b/app/src/main/jni/badvpn/structure/SLinkedList.h new file mode 100644 index 0000000..1edb016 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SLinkedList.h @@ -0,0 +1,38 @@ +/** + * @file SLinkedList.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SLINKEDLIST_H +#define BADVPN_SLINKEDLIST_H + +#include + +#include +#include + +#endif diff --git a/app/src/main/jni/badvpn/structure/SLinkedList_decl.h b/app/src/main/jni/badvpn/structure/SLinkedList_decl.h new file mode 100644 index 0000000..3b366d6 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SLinkedList_decl.h @@ -0,0 +1,61 @@ +/** + * @file SLinkedList_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +typedef struct { + SLinkedListEntry *first; +#if SLINKEDLIST_PARAM_FEATURE_LAST + SLinkedListEntry *last; +#endif +} SLinkedList; + +typedef struct { + SLinkedListEntry *prev; + SLinkedListEntry *next; +} SLinkedListNode; + +static SLinkedListEntry * SLinkedListNext (SLinkedListEntry *entry); +static SLinkedListEntry * SLinkedListPrev (SLinkedListEntry *entry); + +static void SLinkedList_Init (SLinkedList *o); +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry); +#endif +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry); +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry); +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry); +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o); +#endif +static int SLinkedList_IsEmpty (const SLinkedList *o); + +#include "SLinkedList_footer.h" diff --git a/app/src/main/jni/badvpn/structure/SLinkedList_footer.h b/app/src/main/jni/badvpn/structure/SLinkedList_footer.h new file mode 100644 index 0000000..014b87f --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SLinkedList_footer.h @@ -0,0 +1,53 @@ +/** + * @file SLinkedList_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SLINKEDLIST_PARAM_NAME +#undef SLINKEDLIST_PARAM_FEATURE_LAST +#undef SLINKEDLIST_PARAM_TYPE_ENTRY +#undef SLINKEDLIST_PARAM_MEMBER_NODE + +#undef SLinkedList +#undef SLinkedListEntry +#undef SLinkedListNode + +#undef SLinkedListNext +#undef SLinkedListPrev + +#undef SLinkedList_Init +#undef SLinkedList_Prepend +#undef SLinkedList_Append +#undef SLinkedList_InsertBefore +#undef SLinkedList_InsertAfter +#undef SLinkedList_Remove +#undef SLinkedList_First +#undef SLinkedList_Last +#undef SLinkedList_IsEmpty + +#undef SLinkedList_prev +#undef SLinkedList_next diff --git a/app/src/main/jni/badvpn/structure/SLinkedList_header.h b/app/src/main/jni/badvpn/structure/SLinkedList_header.h new file mode 100644 index 0000000..0662d7d --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SLinkedList_header.h @@ -0,0 +1,58 @@ +/** + * @file SLinkedList_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// SLINKEDLIST_PARAM_NAME - name of this data structure +// SLINKEDLIST_PARAM_FEATURE_LAST - whether to keep track of the last entry in the list (0/1) +// SLINKEDLIST_PARAM_TYPE_ENTRY - type of entry +// SLINKEDLIST_PARAM_MEMBER_NODE - name of the node member in entry + +// types +#define SLinkedList SLINKEDLIST_PARAM_NAME +#define SLinkedListEntry SLINKEDLIST_PARAM_TYPE_ENTRY +#define SLinkedListNode MERGE(SLinkedList, Node) + +// non-object public functions +#define SLinkedListNext MERGE(SLinkedList, Next) +#define SLinkedListPrev MERGE(SLinkedList, Prev) + +// public functions +#define SLinkedList_Init MERGE(SLinkedList, _Init) +#define SLinkedList_Prepend MERGE(SLinkedList, _Prepend) +#define SLinkedList_Append MERGE(SLinkedList, _Append) +#define SLinkedList_InsertBefore MERGE(SLinkedList, _InsertBefore) +#define SLinkedList_InsertAfter MERGE(SLinkedList, _InsertAfter) +#define SLinkedList_Remove MERGE(SLinkedList, _Remove) +#define SLinkedList_First MERGE(SLinkedList, _First) +#define SLinkedList_Last MERGE(SLinkedList, _Last) +#define SLinkedList_IsEmpty MERGE(SLinkedList, _IsEmpty) + +// private things +#define SLinkedList_prev(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . prev) +#define SLinkedList_next(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . next) diff --git a/app/src/main/jni/badvpn/structure/SLinkedList_impl.h b/app/src/main/jni/badvpn/structure/SLinkedList_impl.h new file mode 100644 index 0000000..c69d196 --- /dev/null +++ b/app/src/main/jni/badvpn/structure/SLinkedList_impl.h @@ -0,0 +1,151 @@ +/** + * @file SLinkedList_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +static SLinkedListEntry * SLinkedListNext (SLinkedListEntry *entry) +{ + ASSERT(entry) + + return SLinkedList_next(entry); +} + +static SLinkedListEntry * SLinkedListPrev (SLinkedListEntry *entry) +{ + ASSERT(entry) + + return SLinkedList_prev(entry); +} + +static void SLinkedList_Init (SLinkedList *o) +{ + o->first = NULL; +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = NULL; +#endif +} + +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_prev(entry) = NULL; + SLinkedList_next(entry) = o->first; + if (o->first) { + SLinkedList_prev(o->first) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + o->first = entry; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_next(entry) = NULL; + SLinkedList_prev(entry) = o->last; + if (o->last) { + SLinkedList_next(o->last) = entry; + } else { + o->first = entry; + } + o->last = entry; +} +#endif + +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry) +{ + ASSERT(entry) + ASSERT(before_entry) + + SLinkedList_prev(entry) = SLinkedList_prev(before_entry); + SLinkedList_next(entry) = before_entry; + if (SLinkedList_prev(before_entry)) { + SLinkedList_next(SLinkedList_prev(before_entry)) = entry; + } else { + o->first = entry; + } + SLinkedList_prev(before_entry) = entry; +} + +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry) +{ + ASSERT(entry) + ASSERT(after_entry) + + SLinkedList_next(entry) = SLinkedList_next(after_entry); + SLinkedList_prev(entry) = after_entry; + if (SLinkedList_next(after_entry)) { + SLinkedList_prev(SLinkedList_next(after_entry)) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + SLinkedList_next(after_entry) = entry; +} + +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry) +{ + if (SLinkedList_prev(entry)) { + SLinkedList_next(SLinkedList_prev(entry)) = SLinkedList_next(entry); + } else { + o->first = SLinkedList_next(entry); + } + if (SLinkedList_next(entry)) { + SLinkedList_prev(SLinkedList_next(entry)) = SLinkedList_prev(entry); + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = SLinkedList_prev(entry); +#endif + } +} + +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o) +{ + return o->first; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o) +{ + return o->last; +} +#endif + +static int SLinkedList_IsEmpty (const SLinkedList *o) +{ + return !o->first; +} + +#include "SLinkedList_footer.h" diff --git a/app/src/main/jni/badvpn/system/BAddr.h b/app/src/main/jni/badvpn/system/BAddr.h new file mode 100644 index 0000000..a5f3f10 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BAddr.h @@ -0,0 +1,808 @@ +/** + * @file BAddr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network address abstractions. + */ + +#ifndef BADVPN_SYSTEM_BADDR_H +#define BADVPN_SYSTEM_BADDR_H + +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define BADDR_TYPE_NONE 0 +#define BADDR_TYPE_IPV4 1 +#define BADDR_TYPE_IPV6 2 +#ifdef BADVPN_LINUX + #define BADDR_TYPE_PACKET 5 +#endif + +#define BADDR_MAX_ADDR_LEN 128 + +#define BIPADDR_MAX_PRINT_LEN 40 +#define BADDR_MAX_PRINT_LEN 120 + +#define BADDR_PACKET_HEADER_TYPE_ETHERNET 1 + +#define BADDR_PACKET_PACKET_TYPE_HOST 1 +#define BADDR_PACKET_PACKET_TYPE_BROADCAST 2 +#define BADDR_PACKET_PACKET_TYPE_MULTICAST 3 +#define BADDR_PACKET_PACKET_TYPE_OTHERHOST 4 +#define BADDR_PACKET_PACKET_TYPE_OUTGOING 5 + +typedef struct { + int type; + union { + uint32_t ipv4; + uint8_t ipv6[16]; + }; +} BIPAddr; + +static void BIPAddr_InitInvalid (BIPAddr *addr); + +static void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip); + +static void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip); + +static void BIPAddr_Assert (BIPAddr *addr); + +static int BIPAddr_IsInvalid (BIPAddr *addr); + +static int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) WARN_UNUSED; + +static int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2); + +/** + * Converts an IP address to human readable form. + * + * @param addr IP address to convert + * @param out destination buffer. Must be at least BIPADDR_MAX_PRINT_LEN characters long. + */ +static void BIPAddr_Print (BIPAddr *addr, char *out); + +/** + * Socket address - IP address and transport protocol port number + */ +typedef struct { + int type; + union { + struct { + uint32_t ip; + uint16_t port; + } ipv4; + struct { + uint8_t ip[16]; + uint16_t port; + } ipv6; + struct { + uint16_t phys_proto; + int interface_index; + int header_type; + int packet_type; + uint8_t phys_addr[8]; + } packet; + }; +} BAddr; + +/** + * Makes an invalid address. + */ +static BAddr BAddr_MakeNone (void); + +/** + * Makes an IPv4 address. + * + * @param ip IP address in network byte order + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port); + +/** + * Makes an IPv6 address. + * + * @param ip IP address (16 bytes) + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port); + +/** + * Makes an address from a BIPAddr and port number. + * + * @param ipaddr the BIPAddr + * @param port port number in network byte order + */ +static BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port); + +/** + * Deprecated, use BAddr_MakeNone. + */ +static void BAddr_InitNone (BAddr *addr); + +/** + * Deprecated, use BAddr_MakeIPv4. + */ +static void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeIPv6. + */ +static void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeFromIpaddrAndPort. + */ +static void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port); + +/** + * Initializes a packet socket (data link layer) address. + * Only Ethernet addresses are supported. + * + * @param addr the object + * @param phys_proto identifier for the upper protocol, network byte order (EtherType) + * @param interface_index network interface index + * @param header_type data link layer header type. Must be BADDR_PACKET_HEADER_TYPE_ETHERNET. + * @param packet_type the manner in which packets are sent/received. Must be one of + * BADDR_PACKET_PACKET_TYPE_HOST, BADDR_PACKET_PACKET_TYPE_BROADCAST, + * BADDR_PACKET_PACKET_TYPE_MULTICAST, BADDR_PACKET_PACKET_TYPE_OTHERHOST, + * BADDR_PACKET_PACKET_TYPE_OUTGOING. + * @param phys_addr data link layer address (MAC address) + */ +static void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr); + +/** + * Does nothing. + * + * @param addr the object + */ +static void BAddr_Assert (BAddr *addr); + +/** + * Determines whether the address is an invalid address. + * + * @param addr the object + * @return 1 if invalid, 0 if invalid + **/ +static int BAddr_IsInvalid (BAddr *addr); + +/** + * Returns the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @return port number, in network byte order + */ +static uint16_t BAddr_GetPort (BAddr *addr); + +/** + * Returns the IP address in the address. + * + * @param addr the object + * @param ipaddr IP address will be returned here. If \a addr is not + * an IPv4 or IPv6 address, an invalid address will be + * returned. + */ +static void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr); + +/** + * Sets the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @param port port number, in network byte order + */ +static void BAddr_SetPort (BAddr *addr, uint16_t port); + +/** + * Converts an IP address to human readable form. + * + * @param addr address to convert + * @param out destination buffer. Must be at least BADDR_MAX_PRINT_LEN characters long. + */ +static void BAddr_Print (BAddr *addr, char *out); + +/** + * Resolves an address string. + * Format is "addr:port" for IPv4, "[addr]:port" for IPv6. + * addr is be a numeric address or a name. + * port is a numeric port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @param noresolve only accept numeric addresses. Avoids blocking the caller. + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) WARN_UNUSED; + +/** + * Resolves an address string. + * IPv4 input format is "a.b.c.d:p", where a.b.c.d is the IP address + * and d is the port number. + * IPv6 input format is "[addr]:p", where addr is an IPv6 addres in + * standard notation and p is the port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) WARN_UNUSED; + +static int BAddr_Compare (BAddr *addr1, BAddr *addr2); + +static int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2); + +void BIPAddr_InitInvalid (BIPAddr *addr) +{ + addr->type = BADDR_TYPE_NONE; +} + +void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip) +{ + addr->type = BADDR_TYPE_IPV4; + addr->ipv4 = ip; +} + +void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip) +{ + addr->type = BADDR_TYPE_IPV6; + memcpy(addr->ipv6, ip, 16); +} + +void BIPAddr_Assert (BIPAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + return; + default: + ASSERT(0); + } +} + +int BIPAddr_IsInvalid (BIPAddr *addr) +{ + BIPAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) +{ + int len = strlen(str); + + char *addr_start; + int addr_len; + + // determine address type + if (len >= 1 && str[0] == '[' && str[len - 1] == ']') { + addr->type = BADDR_TYPE_IPV6; + addr_start = str + 1; + addr_len = len - 2; + } else { + addr->type = BADDR_TYPE_IPV4; + addr_start = str; + addr_len = len; + } + + // copy + char addr_str[BADDR_MAX_ADDR_LEN + 1]; + if (addr_len > BADDR_MAX_ADDR_LEN) { + return 0; + } + memcpy(addr_str, addr_start, addr_len); + addr_str[addr_len] = '\0'; + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6)); + break; + case BADDR_TYPE_IPV4: + addr->ipv4 = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + break; + } + + freeaddrinfo(addrs); + + return 1; +} + +int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2) +{ + BIPAddr_Assert(addr1); + BIPAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: + return 0; + case BADDR_TYPE_IPV4: + return (addr1->ipv4 == addr2->ipv4); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6, addr2->ipv6, sizeof(addr1->ipv6))); + default: + ASSERT(0) + return 0; + } +} + +uint16_t BAddr_GetPort (BAddr *addr) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + return addr->ipv4.port; + case BADDR_TYPE_IPV6: + return addr->ipv6.port; + default: + ASSERT(0) + return 0; + } +} + +void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr) +{ + BAddr_Assert(addr); + + switch (addr->type) { + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(ipaddr, addr->ipv4.ip); + return; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(ipaddr, addr->ipv6.ip); + return; + default: + BIPAddr_InitInvalid(ipaddr); + } +} + +void BAddr_SetPort (BAddr *addr, uint16_t port) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + addr->ipv4.port = port; + break; + case BADDR_TYPE_IPV6: + addr->ipv6.port = port; + break; + default: + ASSERT(0); + } +} + +void BIPAddr_Print (BIPAddr *addr, char *out) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, + *((uint8_t *)&addr->ipv4 + 0), + *((uint8_t *)&addr->ipv4 + 1), + *((uint8_t *)&addr->ipv4 + 2), + *((uint8_t *)&addr->ipv4 + 3) + ); + break; + case BADDR_TYPE_IPV6: { + const char *ptr = (const char *)addr->ipv6; + sprintf(out, + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16":" + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16, + badvpn_read_be16(ptr + 0), + badvpn_read_be16(ptr + 2), + badvpn_read_be16(ptr + 4), + badvpn_read_be16(ptr + 6), + badvpn_read_be16(ptr + 8), + badvpn_read_be16(ptr + 10), + badvpn_read_be16(ptr + 12), + badvpn_read_be16(ptr + 14) + ); + } break; + default: + ASSERT(0); + } +} + +BAddr BAddr_MakeNone (void) +{ + BAddr addr; + addr.type = BADDR_TYPE_NONE; + return addr; +} + +BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV4; + addr.ipv4.ip = ip; + addr.ipv4.port = port; + return addr; +} + +BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV6; + memcpy(addr.ipv6.ip, ip, 16); + addr.ipv6.port = port; + return addr; +} + +BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + switch (ipaddr.type) { + case BADDR_TYPE_NONE: + return BAddr_MakeNone(); + case BADDR_TYPE_IPV4: + return BAddr_MakeIPv4(ipaddr.ipv4, port); + case BADDR_TYPE_IPV6: + return BAddr_MakeIPv6(ipaddr.ipv6, port); + default: + ASSERT(0); + return BAddr_MakeNone(); + } +} + +void BAddr_InitNone (BAddr *addr) +{ + *addr = BAddr_MakeNone(); +} + +void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port) +{ + *addr = BAddr_MakeIPv4(ip, port); +} + +void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port) +{ + *addr = BAddr_MakeIPv6(ip, port); +} + +void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + *addr = BAddr_MakeFromIpaddrAndPort(ipaddr, port); +} + +#ifdef BADVPN_LINUX + +void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr) +{ + ASSERT(header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + ASSERT(packet_type == BADDR_PACKET_PACKET_TYPE_HOST || packet_type == BADDR_PACKET_PACKET_TYPE_BROADCAST || + packet_type == BADDR_PACKET_PACKET_TYPE_MULTICAST || packet_type == BADDR_PACKET_PACKET_TYPE_OTHERHOST || + packet_type == BADDR_PACKET_PACKET_TYPE_OUTGOING) + + addr->type = BADDR_TYPE_PACKET; + addr->packet.phys_proto = phys_proto; + addr->packet.interface_index = interface_index; + addr->packet.header_type = header_type; + addr->packet.packet_type = packet_type; + memcpy(addr->packet.phys_addr, phys_addr, 6); +} + +#endif + +void BAddr_Assert (BAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + #endif + return; + default: + ASSERT(0); + } +} + +int BAddr_IsInvalid (BAddr *addr) +{ + BAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +void BAddr_Print (BAddr *addr, char *out) +{ + BAddr_Assert(addr); + + BIPAddr ipaddr; + + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(&ipaddr, addr->ipv4.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv4.port)); + break; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(&ipaddr, addr->ipv6.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv6.port)); + break; + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + sprintf(out, "proto=%"PRIu16",ifindex=%d,htype=eth,ptype=%d,addr=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8, + addr->packet.phys_proto, (int)addr->packet.interface_index, (int)addr->packet.packet_type, + addr->packet.phys_addr[0], addr->packet.phys_addr[1], addr->packet.phys_addr[2], + addr->packet.phys_addr[3], addr->packet.phys_addr[4], addr->packet.phys_addr[5]); + break; + #endif + default: + ASSERT(0); + } +} + +int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) +{ + int len = strlen(str); + if (len < 1 || len > 1000) { + return 0; + } + + int addr_start; + int addr_len; + int port_start; + int port_len; + + // leading '[' indicates an IPv6 address + if (str[0] == '[') { + addr->type = BADDR_TYPE_IPV6; + // find ']' + int i=1; + while (i < len && str[i] != ']') i++; + if (i >= len) { + return 0; + } + addr_start = 1; + addr_len = i - addr_start; + // follows ':' and port number + if (i + 1 >= len || str[i + 1] != ':') { + return 0; + } + port_start = i + 2; + port_len = len - port_start; + } + // otherwise it's an IPv4 address + else { + addr->type = BADDR_TYPE_IPV4; + // find ':' + int i=0; + while (i < len && str[i] != ':') i++; + if (i >= len) { + return 0; + } + addr_start = 0; + addr_len = i - addr_start; + port_start = i + 1; + port_len = len - port_start; + } + + // copy address and port to zero-terminated buffers + + char addr_str[128]; + if (addr_len >= sizeof(addr_str)) { + return 0; + } + memcpy(addr_str, str + addr_start, addr_len); + addr_str[addr_len] = '\0'; + + char port_str[6]; + if (port_len >= sizeof(port_str)) { + return 0; + } + memcpy(port_str, str + port_start, port_len); + port_str[port_len] = '\0'; + + // parse port + char *err; + long int conv_res = strtol(port_str, &err, 10); + if (port_str[0] == '\0' || *err != '\0') { + return 0; + } + if (conv_res < 0 || conv_res > UINT16_MAX) { + return 0; + } + uint16_t port = conv_res; + port = hton16(port); + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6.ip, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6.ip)); + addr->ipv6.port = port; + break; + case BADDR_TYPE_IPV4: + addr->ipv4.ip = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + addr->ipv4.port = port; + break; + } + + freeaddrinfo(addrs); + + if (name) { + if (strlen(addr_str) >= name_len) { + return 0; + } + strcpy(name, addr_str); + } + + return 1; +} + +int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) +{ + return BAddr_Parse2(addr, str, name, name_len, 0); +} + +int BAddr_Compare (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_IPV4: + return (addr1->ipv4.ip == addr2->ipv4.ip && addr1->ipv4.port == addr2->ipv4.port); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)) && addr1->ipv6.port == addr2->ipv6.port); + default: + return 0; + } +} + +int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + int cmp = B_COMPARE(addr1->type, addr2->type); + if (cmp) { + return cmp; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: { + return 0; + } break; + case BADDR_TYPE_IPV4: { + uint32_t ip1 = ntoh32(addr1->ipv4.ip); + uint32_t ip2 = ntoh32(addr2->ipv4.ip); + cmp = B_COMPARE(ip1, ip2); + if (cmp) { + return cmp; + } + uint16_t port1 = ntoh16(addr1->ipv4.port); + uint16_t port2 = ntoh16(addr2->ipv4.port); + return B_COMPARE(port1, port2); + } break; + case BADDR_TYPE_IPV6: { + cmp = memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)); + if (cmp) { + return B_COMPARE(cmp, 0); + } + uint16_t port1 = ntoh16(addr1->ipv6.port); + uint16_t port2 = ntoh16(addr2->ipv6.port); + return B_COMPARE(port1, port2); + } break; + default: { + return 0; + } break; + } +} + +#endif diff --git a/app/src/main/jni/badvpn/system/BConnection.h b/app/src/main/jni/badvpn/system/BConnection.h new file mode 100644 index 0000000..eac25ec --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnection.h @@ -0,0 +1,369 @@ +/** + * @file BConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BCONNECTION +#define BADVPN_SYSTEM_BCONNECTION + +#include +#include +#include +#include +#include +#include + + + +/** + * Checks if the given address is supported by {@link BConnection} and related objects. + * + * @param addr address to check. Must be a proper {@link BAddr} object according to + * {@link BIPAddr_Assert}. + * @return 1 if supported, 0 if not + */ +int BConnection_AddressSupported (BAddr addr); + + + +struct BListener_s; + +/** + * Object which listens for connections on an address. + * When a connection is ready, the {@link BListener_handler} handler is called, from which + * the connection can be accepted into a new {@link BConnection} object. + */ +typedef struct BListener_s BListener; + +/** + * Handler called when a new connection is ready. + * The connection can be accepted by calling {@link BConnection_Init} with the a + * BCONNECTION_SOURCE_LISTENER 'source' argument. + * If no attempt is made to accept the connection from the job closure of this handler, + * the connection will be discarded. + * + * @param user as in {@link BListener_Init} + */ +typedef void (*BListener_handler) (void *user); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to listen on + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for listening on a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for listening + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BListener_Free (BListener *o); + + + +struct BConnector_s; + +/** + * Object which connects to an address. + * When the connection attempt finishes, the {@link BConnector_handler} handler is called, from which, + * if successful, the resulting connection can be moved to a new {@link BConnection} object. + */ +typedef struct BConnector_s BConnector; + +/** + * Handler called when the connection attempt finishes. + * If the connection attempt succeeded (is_error==0), the new connection can be used by calling + * {@link BConnection_Init} with a BCONNECTION_SOURCE_TYPE_CONNECTOR 'source' argument. + * This handler will be called at most once. The connector object need not be freed after it + * is called. + * + * @param user as in {@link BConnector_Init} + * @param is_error whether the connection attempt succeeded (0) or failed (1) + */ +typedef void (*BConnector_handler) (void *user, int is_error); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to connect to + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for connecting to a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for connecting + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BConnector_Free (BConnector *o); + + + +#define BCONNECTION_SOURCE_TYPE_LISTENER 1 +#define BCONNECTION_SOURCE_TYPE_CONNECTOR 2 +#define BCONNECTION_SOURCE_TYPE_PIPE 3 + +struct BConnection_source { + int type; + union { + struct { + BListener *listener; + BAddr *out_addr; + } listener; + struct { + BConnector *connector; + } connector; +#ifndef BADVPN_USE_WINAPI + struct { + int pipefd; + } pipe; +#endif + } u; +}; + +static struct BConnection_source BConnection_source_listener (BListener *listener, BAddr *out_addr) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_LISTENER; + s.u.listener.listener = listener; + s.u.listener.out_addr = out_addr; + return s; +} + +static struct BConnection_source BConnection_source_connector (BConnector *connector) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_CONNECTOR; + s.u.connector.connector = connector; + return s; +} + +#ifndef BADVPN_USE_WINAPI +static struct BConnection_source BConnection_source_pipe (int pipefd) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_PIPE; + s.u.pipe.pipefd = pipefd; + return s; +} +#endif + +struct BConnection_s; + +/** + * Object which represents a stream connection. This is usually a TCP connection, either client + * or server, but may also be used with any file descriptor (e.g. pipe) on Unix-like systems. + * Sending and receiving is performed via {@link StreamPassInterface} and {@link StreamRecvInterface}, + * respectively. + */ +typedef struct BConnection_s BConnection; + +#define BCONNECTION_EVENT_ERROR 1 +#define BCONNECTION_EVENT_RECVCLOSED 2 + +/** + * Handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * - If event is BCONNECTION_EVENT_ERROR, the connection is no longer usable and must be freed + * from withing the job closure of this handler. No further I/O or interface initialization + * must occur. + * - If event is BCONNECTION_EVENT_RECVCLOSED, no further receive I/O or receive interface + * initialization must occur. It is guarantted that the receive interface was initialized. + * + * @param user as in {@link BConnection_Init} or {@link BConnection_SetHandlers} + * @param event what happened - BCONNECTION_EVENT_ERROR or BCONNECTION_EVENT_RECVCLOSED + */ +typedef void (*BConnection_handler) (void *user, int event); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param source specifies what the connection comes from. This argument must be created with one of the + * following macros: + * - BCONNECTION_SOURCE_LISTENER(BListener *, BAddr *) + * Accepts a connection ready on a {@link BListener} object. Must be called from the job + * closure of the listener's {@link BListener_handler}, and must be the first attempt + * for this handler invocation. The address of the client is written if the address + * argument is not NULL (theoretically an invalid address may be returned). + * - BCONNECTION_SOURCE_CONNECTOR(Bconnector *) + * Uses a connection establised with {@link BConnector}. Must be called from the job + * closure of the connector's {@link BConnector_handler}, the handler must be reporting + * successful connection, and must be the first attempt for this handler invocation. + * - BCONNECTION_SOURCE_PIPE(int) + * On Unix-like systems, uses the provided file descriptor. The file descriptor number must + * be >=0. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * @return 1 on success, 0 on failure + */ +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * If the connection was created with a BCONNECTION_SOURCE_PIPE 'source' argument, the file descriptor + * will not be closed. + * + * @param o the object + */ +void BConnection_Free (BConnection *o); + +/** + * Updates the handler function. + * + * @param o the object + * @param user argument to handler + * @param handler new handler function, as in {@link BConnection_Init}. Additionally, may be NULL to + * remove the current handler. In this case, a proper handler must be set before anything + * can happen with the connection. This is used when moving the connection ownership from + * one module to another. + */ +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler); + +/** + * Sets the SO_SNDBUF socket option. + * + * @param o the object + * @param buf_size value for SO_SNDBUF option + * @return 1 on success, 0 on failure + */ +int BConnection_SetSendBuffer (BConnection *o, int buf_size); + +/** + * Initializes the send interface for the connection. + * The send interface must not be initialized. + * + * @param o the object + */ +void BConnection_SendAsync_Init (BConnection *o); + +/** + * Frees the send interface for the connection. + * The send interface must be initialized. + * If the send interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_SendAsync_Free (BConnection *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o); + +/** + * Initializes the receive interface for the connection. + * The receive interface must not be initialized. + * + * @param o the object + */ +void BConnection_RecvAsync_Init (BConnection *o); + +/** + * Frees the receive interface for the connection. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_RecvAsync_Free (BConnection *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o); + + + +#ifdef BADVPN_USE_WINAPI +#include "BConnection_win.h" +#else +#include "BConnection_unix.h" +#endif + +#endif diff --git a/app/src/main/jni/badvpn/system/BConnectionGeneric.h b/app/src/main/jni/badvpn/system/BConnectionGeneric.h new file mode 100644 index 0000000..9417555 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnectionGeneric.h @@ -0,0 +1,144 @@ +/** + * @file BConnectionGeneric.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BCONNECTION_GENERIC_H +#define BADVPN_BCONNECTION_GENERIC_H + +#include + +#include +#include +#include +#include + +#define BCONNECTION_ADDR_TYPE_BADDR 1 +#define BCONNECTION_ADDR_TYPE_UNIX 2 + +struct BConnection_addr { + int type; + union { + BAddr baddr; + struct { + const char *str; + size_t len; + } unix_socket_path; + } u; +}; + +static struct BConnection_addr BConnection_addr_baddr (BAddr baddr) +{ + struct BConnection_addr addr; + addr.type = BCONNECTION_ADDR_TYPE_BADDR; + addr.u.baddr = baddr; + return addr; +} + +static struct BConnection_addr BConnection_addr_unix (const char *unix_socket_path, size_t len) +{ + struct BConnection_addr addr; + addr.type = BCONNECTION_ADDR_TYPE_UNIX; + addr.u.unix_socket_path.str = unix_socket_path; + addr.u.unix_socket_path.len = len; + return addr; +} + +static int BListener_InitGeneric (BListener *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; +static int BConnector_InitGeneric (BConnector *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; + +static int BListener_InitGeneric (BListener *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + ASSERT(addr.type != BCONNECTION_ADDR_TYPE_UNIX || !memchr(addr.u.unix_socket_path.str, '\0', addr.u.unix_socket_path.len)) + + switch (addr.type) { + case BCONNECTION_ADDR_TYPE_BADDR: { + return BListener_Init(o, addr.u.baddr, reactor, user, handler); + } break; + + case BCONNECTION_ADDR_TYPE_UNIX: { +#ifdef BADVPN_USE_WINAPI + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "unix sockets not supported"); + return 0; +#else + char *str = b_strdup_bin(addr.u.unix_socket_path.str, addr.u.unix_socket_path.len); + if (!str) { + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "b_strdup_bin failed"); + return 0; + } + int res = BListener_InitUnix(o, str, reactor, user, handler); + free(str); + return res; +#endif + } break; + + default: + ASSERT(0); + return 0; + } +} + +static int BConnector_InitGeneric (BConnector *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + ASSERT(addr.type != BCONNECTION_ADDR_TYPE_UNIX || !memchr(addr.u.unix_socket_path.str, '\0', addr.u.unix_socket_path.len)) + + switch (addr.type) { + case BCONNECTION_ADDR_TYPE_BADDR: { + return BConnector_Init(o, addr.u.baddr, reactor, user, handler); + } break; + + case BCONNECTION_ADDR_TYPE_UNIX: { +#ifdef BADVPN_USE_WINAPI + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "unix sockets not supported"); + return 0; +#else + char *str = b_strdup_bin(addr.u.unix_socket_path.str, addr.u.unix_socket_path.len); + if (!str) { + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "b_strdup_bin failed"); + return 0; + } + int res = BConnector_InitUnix(o, str, reactor, user, handler); + free(str); + return res; +#endif + } break; + + default: + ASSERT(0); + return 0; + } +} + +#endif diff --git a/app/src/main/jni/badvpn/system/BConnection_unix.c b/app/src/main/jni/badvpn/system/BConnection_unix.c new file mode 100644 index 0000000..f6fa356 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnection_unix.c @@ -0,0 +1,1057 @@ +/** + * @file BConnection_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "BConnection.h" + +#include + +#define MAX_UNIX_SOCKET_PATH 200 + +#define SEND_STATE_NOT_INITED 0 +#define SEND_STATE_READY 1 +#define SEND_STATE_BUSY 2 + +#define RECV_STATE_NOT_INITED 0 +#define RECV_STATE_READY 1 +#define RECV_STATE_BUSY 2 +#define RECV_STATE_INITED_CLOSED 3 +#define RECV_STATE_NOT_INITED_CLOSED 4 + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct unix_addr { + socklen_t len; + union { + struct sockaddr_un addr; + uint8_t bytes[offsetof(struct sockaddr_un, sun_path) + MAX_UNIX_SOCKET_PATH + 1]; + } u; +}; + +static int build_unix_address (struct unix_addr *out, const char *socket_path); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void listener_fd_handler (BListener *o, int events); +static void listener_default_job_handler (BListener *o); +static void connector_fd_handler (BConnector *o, int events); +static void connector_job_handler (BConnector *o); +static void connection_report_error (BConnection *o); +static void connection_send (BConnection *o); +static void connection_recv (BConnection *o); +static void connection_fd_handler (BConnection *o, int events); +static void connection_send_job_handler (BConnection *o); +static void connection_recv_job_handler (BConnection *o); +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len); +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_len); + +static int build_unix_address (struct unix_addr *out, const char *socket_path) +{ + ASSERT(socket_path); + + if (strlen(socket_path) > MAX_UNIX_SOCKET_PATH) { + return 0; + } + + out->len = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path) + 1; + out->u.addr.sun_family = AF_UNIX; + strcpy(out->u.addr.sun_path, socket_path); + + return 1; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void listener_fd_handler (BListener *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // set default job + BPending_Set(&o->default_job); + + // call handler + o->handler(o->user); + return; +} + +static void listener_default_job_handler (BListener *o) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "discarding connection"); + + // accept + int newfd = accept(o->fd, NULL, NULL); + if (newfd < 0) { + BLog(BLOG_ERROR, "accept failed"); + return; + } + + // close new fd + if (close(newfd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +static void connector_fd_handler (BConnector *o, int events) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(!o->connected) + ASSERT(o->have_bfd) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // set have no BFileDescriptor + o->have_bfd = 0; + + // read connection result + int result; + socklen_t result_len = sizeof(result); + if (getsockopt(o->fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) { + BLog(BLOG_ERROR, "getsockopt failed"); + goto fail0; + } + ASSERT_FORCE(result_len == sizeof(result)) + + if (result != 0) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // set connected + o->connected = 1; + +fail0: + // call handler + o->handler(o->user, !o->connected); + return; +} + +static void connector_job_handler (BConnector *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(o->connected) + ASSERT(!o->have_bfd) + + // call handler + o->handler(o->user, 0); + return; +} + +static void connection_report_error (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->handler) + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR)); + return; +} + +static void connection_send (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // send + int bytes = write(o->fd, o->send.busy_data, o->send.busy_data_len); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + connection_report_error(o); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->send.busy_data_len) + + // set ready + o->send.state = SEND_STATE_READY; + + // done + StreamPassInterface_Done(&o->send.iface, bytes); +} + +static void connection_recv (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // recv + int bytes = read(o->fd, o->recv.busy_data, o->recv.busy_data_avail); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + connection_report_error(o); + return; + } + + if (bytes == 0) { + // set recv inited closed + o->recv.state = RECV_STATE_INITED_CLOSED; + + // report recv closed + o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->recv.busy_data_avail) + + // set not busy + o->recv.state = RECV_STATE_READY; + + // done + StreamRecvInterface_Done(&o->recv.iface, bytes); +} + +static void connection_fd_handler (BConnection *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->is_hupd) + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + // if we got a HUP event, stop monitoring the file descriptor + if ((events & BREACTOR_HUP)) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + o->is_hupd = 1; + } + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.state == SEND_STATE_BUSY)) { + ASSERT(o->send.state == SEND_STATE_BUSY) + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.state == RECV_STATE_BUSY)) { + ASSERT(o->recv.state == RECV_STATE_BUSY) + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + connection_send(o); + return; + } + + if (have_recv) { + connection_recv(o); + return; + } + + if (!o->is_hupd) { + BLog(BLOG_ERROR, "fd error event"); + connection_report_error(o); + return; + } +} + +static void connection_send_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + connection_send(o); + return; +} + +static void connection_recv_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + connection_recv(o); + return; +} + +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_READY) + ASSERT(data_len > 0) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.state = SEND_STATE_BUSY; + + connection_send(o); + return; +} + +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_avail) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_READY) + ASSERT(data_avail > 0) + + // remember data + o->recv.busy_data = data; + o->recv.busy_data_avail = data_avail; + + // set busy + o->recv.state = RECV_STATE_BUSY; + + connection_recv(o); + return; +} + +int BConnection_AddressSupported (BAddr addr) +{ + BAddr_Assert(&addr); + + return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6); +} + +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // set no unix socket path + o->unix_socket_path = NULL; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // set SO_REUSEADDR + int optval = 1; + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + BLog(BLOG_ERROR, "setsockopt(SO_REUSEADDR) failed"); + } + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // copy socket path + o->unix_socket_path = b_strdup(socket_path); + if (!o->unix_socket_path) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail1; + } + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // unlink existing socket + if (unlink(o->unix_socket_path) < 0 && errno != ENOENT) { + BLog(BLOG_ERROR, "unlink existing socket failed"); + goto fail2; + } + + // bind + if (bind(o->fd, (struct sockaddr *)&addr.u.addr, addr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail2; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail3; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail3; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + free(o->unix_socket_path); +fail0: + return 0; +} + +void BListener_Free (BListener *o) +{ + DebugObject_Free(&o->d_obj); + + // free default job + BPending_Free(&o->default_job); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + + // unlink unix socket + if (o->unix_socket_path) { + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } + } + + // free unix socket path + if (o->unix_socket_path) { + free(o->unix_socket_path); + } +} + +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, &sysaddr.addr.generic, sysaddr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail0; + } + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, (struct sockaddr *)&addr.u.addr, addr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +void BConnector_Free (BConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free BFileDescriptor + if (o->have_bfd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->fd != -1) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } + + // free job + BPending_Free(&o->job); +} + +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) +{ + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + DebugObject_Access(&listener->d_obj); + ASSERT(BPending_IsSet(&listener->default_job)) + } break; + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + DebugObject_Access(&connector->d_obj); + ASSERT(connector->fd >= 0) + ASSERT(connector->connected) + ASSERT(!connector->have_bfd) + ASSERT(!BPending_IsSet(&connector->job)) + } break; + case BCONNECTION_SOURCE_TYPE_PIPE: { + ASSERT(source.u.pipe.pipefd >= 0) + } break; + default: ASSERT(0); + } + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + + // unset listener's default job + BPending_Unset(&listener->default_job); + + // accept + struct sys_addr sysaddr; + sysaddr.len = sizeof(sysaddr.addr); + if ((o->fd = accept(listener->fd, &sysaddr.addr.generic, &sysaddr.len)) < 0) { + BLog(BLOG_ERROR, "accept failed"); + goto fail0; + } + o->close_fd = 1; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // return address + if (source.u.listener.out_addr) { + addr_sys_to_socket(source.u.listener.out_addr, sysaddr); + } + } break; + + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + + // grab fd from connector + o->fd = connector->fd; + connector->fd = -1; + o->close_fd = 1; + } break; + + case BCONNECTION_SOURCE_TYPE_PIPE: { + // use user-provided fd + o->fd = source.u.pipe.pipefd; + o->close_fd = 0; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + } break; + } + + // set not HUPd + o->is_hupd = 0; + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connection_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BCONNECTION_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BCONNECTION_RECV_LIMIT); + + // set send and recv not inited + o->send.state = SEND_STATE_NOT_INITED; + o->recv.state = RECV_STATE_NOT_INITED; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +fail0: + return 0; +} + +void BConnection_Free (BConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + ASSERT(o->recv.state == RECV_STATE_NOT_INITED || o->recv.state == RECV_STATE_NOT_INITED_CLOSED) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + if (!o->is_hupd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +} + +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler) +{ + DebugObject_Access(&o->d_obj); + + // set handlers + o->user = user; + o->handler = handler; +} + +int BConnection_SetSendBuffer (BConnection *o, int buf_size) +{ + DebugObject_Access(&o->d_obj); + + if (setsockopt(o->fd, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) { + BLog(BLOG_ERROR, "setsockopt failed"); + return 0; + } + + return 1; +} + +void BConnection_SendAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + + // init interface + StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_send_job_handler, o); + + // set ready + o->send.state = SEND_STATE_READY; +} + +void BConnection_SendAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->send.job); + + // free interface + StreamPassInterface_Free(&o->send.iface); + + // set not inited + o->send.state = SEND_STATE_NOT_INITED; +} + +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + return &o->send.iface; +} + +void BConnection_RecvAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_NOT_INITED) + + // init interface + StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_recv_job_handler, o); + + // set ready + o->recv.state = RECV_STATE_READY; +} + +void BConnection_RecvAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->recv.job); + + // free interface + StreamRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.state = RECV_STATE_NOT_INITED; +} + +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + return &o->recv.iface; +} diff --git a/app/src/main/jni/badvpn/system/BConnection_unix.h b/app/src/main/jni/badvpn/system/BConnection_unix.h new file mode 100644 index 0000000..e134f6c --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnection_unix.h @@ -0,0 +1,87 @@ +/** + * @file BConnection_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BCONNECTION_SEND_LIMIT 2 +#define BCONNECTION_RECV_LIMIT 2 +#define BCONNECTION_LISTEN_BACKLOG 128 + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + char *unix_socket_path; + int fd; + BFileDescriptor bfd; + BPending default_job; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + BPending job; + int fd; + int connected; + int have_bfd; + BFileDescriptor bfd; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + int fd; + int close_fd; + int is_hupd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + StreamPassInterface iface; + BPending job; + const uint8_t *busy_data; + int busy_data_len; + int state; + } send; + struct { + BReactorLimit limit; + StreamRecvInterface iface; + BPending job; + uint8_t *busy_data; + int busy_data_avail; + int state; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/app/src/main/jni/badvpn/system/BConnection_win.c b/app/src/main/jni/badvpn/system/BConnection_win.c new file mode 100644 index 0000000..17ae727 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnection_win.c @@ -0,0 +1,875 @@ +/** + * @file BConnection_win.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "BConnection.h" + +#include + +#define LISTEN_BACKLOG 128 + +struct sys_addr { + int len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_any_to_sys (struct sys_addr *out, int family); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void listener_next_job_handler (BListener *o); +static void listener_olap_handler (BListener *o, int event, DWORD bytes); +static void connector_olap_handler (BConnector *o, int event, DWORD bytes); +static void connector_abort (BConnector *o); +static void connection_report_error (BConnection *o); +static void connection_abort (BConnection *o); +static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len); +static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len); +static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes); +static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes); + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_any_to_sys (struct sys_addr *out, int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = 0; + out->addr.ipv4.sin_addr.s_addr = INADDR_ANY; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = 0; + out->addr.ipv6.sin6_flowinfo = 0; + struct in6_addr any = IN6ADDR_ANY_INIT; + out->addr.ipv6.sin6_addr = any; + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void listener_next_job_handler (BListener *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->busy) + + // free ready socket + if (o->ready) { + BLog(BLOG_ERROR, "discarding connection"); + + // close new socket + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // set not ready + o->ready = 0; + } + + // create new socket + if ((o->newsock = WSASocket(o->sys_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // start accept operation + while (1) { + memset(&o->olap.olap, 0, sizeof(o->olap.olap)); + DWORD bytes; + BOOL res = o->fnAcceptEx(o->sock, o->newsock, o->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub), &bytes, &o->olap.olap); + if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "AcceptEx failed"); + continue; + } + break; + } + + // set busy + o->busy = 1; + + return; + +fail0: + return; +} + +static void listener_olap_handler (BListener *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->busy) + ASSERT(!o->ready) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->busy = 0; + + // schedule next accept + BPending_Set(&o->next_job); + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "accepting failed"); + + // close new socket + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + return; + } + + BLog(BLOG_INFO, "connection accepted"); + + // set ready + o->ready = 1; + + // call handler + o->handler(o->user); + return; +} + +static void connector_olap_handler (BConnector *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sock != INVALID_SOCKET) + ASSERT(o->busy) + ASSERT(!o->ready) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "connection failed"); + } else { + // set ready + o->ready = 1; + } + + // call handler + o->handler(o->user, !o->ready); + return; +} + +static void connector_abort (BConnector *o) +{ + if (o->sock != INVALID_SOCKET) { + // cancel I/O + if (o->busy) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + } + + // wait for connect operation to finish + if (o->busy) { + BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL); + } + + // free olap + BReactorIOCPOverlapped_Free(&o->olap); +} + +static void connection_report_error (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->handler) + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR)); + return; +} + +static void connection_abort (BConnection *o) +{ + ASSERT(!o->aborted) + + // cancel I/O + if ((o->recv.inited && o->recv.busy) || (o->send.inited && o->send.busy)) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for receiving to complete + if (o->recv.inited && o->recv.busy) { + BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL); + } + + // wait for sending to complete + if (o->send.inited && o->send.busy) { + BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv.olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send.olap); + + // set aborted + o->aborted = 1; +} + +static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(!o->send.busy) + ASSERT(data_len > 0) + + if (data_len > ULONG_MAX) { + data_len = ULONG_MAX; + } + + WSABUF buf; + buf.buf = (char *)data; + buf.len = data_len; + + memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap)); + + // send + int res = WSASend(o->sock, &buf, 1, NULL, 0, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASend failed (%d)", WSAGetLastError()); + connection_report_error(o); + return; + } + + // set busy + o->send.busy = 1; + o->send.busy_data_len = data_len; +} + +static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(!o->recv.busy) + ASSERT(data_len > 0) + + if (data_len > ULONG_MAX) { + data_len = ULONG_MAX; + } + + WSABUF buf; + buf.buf = (char *)data; + buf.len = data_len; + + memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap)); + + // recv + DWORD flags = 0; + int res = WSARecv(o->sock, &buf, 1, NULL, &flags, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecv failed (%d)", WSAGetLastError()); + connection_report_error(o); + return; + } + + // set busy + o->recv.busy = 1; + o->recv.busy_data_len = data_len; +} + +static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->send.busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "sending failed"); + connection_report_error(o); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->send.busy_data_len) + + // done + StreamPassInterface_Done(&o->send.iface, bytes); +} + +static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->recv.busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "receiving failed"); + connection_report_error(o); + return; + } + + if (bytes == 0) { + // set closed + o->recv.closed = 1; + + // report recv closed + o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->recv.busy_data_len) + + // done + StreamRecvInterface_Done(&o->recv.iface, bytes); +} + +int BConnection_AddressSupported (BAddr addr) +{ + BAddr_Assert(&addr); + + return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6); +} + +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // remember family + o->sys_family = sysaddr.addr.generic.sa_family; + + // init socket + if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // bind + if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // listen + if (listen(o->sock, LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail1; + } + + DWORD out_bytes; + + // obtain AcceptEx + GUID guid1 = WSAID_ACCEPTEX; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid1, sizeof(guid1), &o->fnAcceptEx, sizeof(o->fnAcceptEx), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to obtain AcceptEx"); + goto fail1; + } + + // obtain GetAcceptExSockaddrs + GUID guid2 = WSAID_GETACCEPTEXSOCKADDRS; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid2, sizeof(guid2), &o->fnGetAcceptExSockaddrs, sizeof(o->fnGetAcceptExSockaddrs), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to obtain GetAcceptExSockaddrs"); + goto fail1; + } + + // init olap + BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)listener_olap_handler); + + // init next job + BPending_Init(&o->next_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_next_job_handler, o); + + // set not busy + o->busy = 0; + + // set not ready + o->ready = 0; + + // set next job + BPending_Set(&o->next_job); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BListener_Free (BListener *o) +{ + DebugObject_Free(&o->d_obj); + + // cancel I/O + if (o->busy) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for accept operation to finish + if (o->busy) { + BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL); + } + + // close new socket + if (o->busy || o->ready) { + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + } + + // free next job + BPending_Free(&o->next_job); + + // free olap + BReactorIOCPOverlapped_Free(&o->olap); +} + +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // create local any address + struct sys_addr local_sysaddr; + addr_any_to_sys(&local_sysaddr, addr.type); + + // init socket + if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // bind socket + if (bind(o->sock, &local_sysaddr.addr.generic, local_sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // obtain ConnectEx + GUID guid = WSAID_CONNECTEX; + DWORD out_bytes; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnConnectEx, sizeof(o->fnConnectEx), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to get ConnectEx"); + goto fail1; + } + + // init olap + BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connector_olap_handler); + + // start connect operation + BOOL res = o->fnConnectEx(o->sock, &sysaddr.addr.generic, sysaddr.len, NULL, 0, NULL, &o->olap.olap); + if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "ConnectEx failed (%d)", WSAGetLastError()); + goto fail2; + } + + // set busy + o->busy = 1; + + // set not ready + o->ready = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + BReactorIOCPOverlapped_Free(&o->olap); +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BConnector_Free (BConnector *o) +{ + DebugObject_Free(&o->d_obj); + + if (o->sock != INVALID_SOCKET) { + connector_abort(o); + } +} + +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) +{ + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + DebugObject_Access(&listener->d_obj); + ASSERT(BPending_IsSet(&listener->next_job)) + ASSERT(!listener->busy) + ASSERT(listener->ready) + } break; + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + DebugObject_Access(&connector->d_obj); + ASSERT(connector->reactor == reactor) + ASSERT(connector->sock != INVALID_SOCKET) + ASSERT(!connector->busy) + ASSERT(connector->ready) + } break; + default: ASSERT(0); + } + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + + // grab new socket from listener + o->sock = listener->newsock; + listener->ready = 0; + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // return address + if (source.u.listener.out_addr) { + struct sockaddr *addr_local; + struct sockaddr *addr_remote; + int len_local; + int len_remote; + listener->fnGetAcceptExSockaddrs(listener->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub), + &addr_local, &len_local, &addr_remote, &len_remote); + + struct sys_addr sysaddr; + + ASSERT_FORCE(len_remote >= 0) + ASSERT_FORCE(len_remote <= sizeof(sysaddr.addr)) + + memcpy((uint8_t *)&sysaddr.addr, (uint8_t *)addr_remote, len_remote); + sysaddr.len = len_remote; + + addr_sys_to_socket(source.u.listener.out_addr, sysaddr); + } + } break; + + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + + // grab fd from connector + o->sock = connector->sock; + connector->sock = INVALID_SOCKET; + + // release connector resources + connector_abort(connector); + } break; + } + + // set not aborted + o->aborted = 0; + + // init send olap + BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_send_olap_handler); + + // set send not inited + o->send.inited = 0; + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_recv_olap_handler); + + // set recv not closed + o->recv.closed = 0; + + // set recv not inited + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + return 0; +} + +void BConnection_Free (BConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + if (!o->aborted) { + connection_abort(o); + } +} + +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler) +{ + DebugObject_Access(&o->d_obj); + + // set handlers + o->user = user; + o->handler = handler; +} + +int BConnection_SetSendBuffer (BConnection *o, int buf_size) +{ + DebugObject_Access(&o->d_obj); + + if (setsockopt(o->sock, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(buf_size)) < 0) { + BLog(BLOG_ERROR, "setsockopt failed"); + return 0; + } + + return 1; +} + +void BConnection_SendAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->send.inited) + + // init interface + StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_iface_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // set not busy + o->send.busy = 0; + + // set inited + o->send.inited = 1; +} + +void BConnection_SendAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // abort if busy + if (o->send.busy && !o->aborted) { + connection_abort(o); + } + + // free interface + StreamPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BConnection_RecvAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(!o->recv.inited) + + // init interface + StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_iface_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set not busy + o->recv.busy = 0; + + // set inited + o->recv.inited = 1; +} + +void BConnection_RecvAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // abort if busy + if (o->recv.busy && !o->aborted) { + connection_abort(o); + } + + // free interface + StreamRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/app/src/main/jni/badvpn/system/BConnection_win.h b/app/src/main/jni/badvpn/system/BConnection_win.h new file mode 100644 index 0000000..da9a8ed --- /dev/null +++ b/app/src/main/jni/badvpn/system/BConnection_win.h @@ -0,0 +1,101 @@ +/** + * @file BConnection_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BListener_addrbuf_stub { + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + uint8_t extra[16]; +}; + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + int sys_family; + SOCKET sock; + LPFN_ACCEPTEX fnAcceptEx; + LPFN_GETACCEPTEXSOCKADDRS fnGetAcceptExSockaddrs; + BReactorIOCPOverlapped olap; + SOCKET newsock; + uint8_t addrbuf[2 * sizeof(struct BListener_addrbuf_stub)]; + BPending next_job; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + SOCKET sock; + LPFN_CONNECTEX fnConnectEx; + BReactorIOCPOverlapped olap; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + SOCKET sock; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int inited; + StreamPassInterface iface; + int busy; + int busy_data_len; + } send; + struct { + BReactorIOCPOverlapped olap; + int closed; + int inited; + StreamRecvInterface iface; + int busy; + int busy_data_len; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/app/src/main/jni/badvpn/system/BDatagram.h b/app/src/main/jni/badvpn/system/BDatagram.h new file mode 100644 index 0000000..33efb45 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BDatagram.h @@ -0,0 +1,209 @@ +/** + * @file BDatagram.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BDATAGRAM +#define BADVPN_SYSTEM_BDATAGRAM + +#include +#include +#include +#include +#include +#include + +struct BDatagram_s; + +/** + * Represents datagram communication. This is usually UDP, but may also be Linux packet sockets. + * Sending and receiving is performed via {@link PacketPassInterface} and {@link PacketRecvInterface}, + * respectively. + */ +typedef struct BDatagram_s BDatagram; + +#define BDATAGRAM_EVENT_ERROR 1 + +/** + * Handler called when an error occurs with the datagram object. + * The datagram object is no longer usable and must be freed from withing the job closure of + * this handler. No further I/O, interface initialization, binding and send address setting + * must occur. + * + * @param user as in {@link BDatagram_Init} + * @param event always BDATAGRAM_EVENT_ERROR + */ +typedef void (*BDatagram_handler) (void *user, int event); + +/** + * Checks if the given address family (from {@link BAddr.h}) is supported by {@link BDatagram} + * and related objects. + * + * @param family family to check + * @return 1 if supported, 0 if not + */ +int BDatagram_AddressFamilySupported (int family); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param family address family. Must be supported according to {@link BDatagram_AddressFamilySupported}. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs + * @return 1 on success, 0 on failure + */ +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * + * @param o the object + */ +void BDatagram_Free (BDatagram *o); + +/** + * Binds to the given local address. + * May initiate I/O. + * + * @param o the object + * @param addr address to bind to. Its family must be supported according to {@link BDatagram_AddressFamilySupported}. + * @return 1 on success, 0 on failure + */ +int BDatagram_Bind (BDatagram *o, BAddr addr) WARN_UNUSED; + +/** + * Sets addresses for sending. + * May initiate I/O. + * + * @param o the object + * @param remote_addr destination address for sending datagrams. Its family must be supported according + * to {@link BDatagram_AddressFamilySupported}. + * @param local_addr local source IP address. May be an invalid address, otherwise its family must be + * supported according to {@link BDatagram_AddressFamilySupported}. + */ +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr); + +/** + * Returns the remote and local address of the last datagram received. + * Fails if and only if no datagrams have been received yet. + * + * @param o the object + * @param remote_addr returns the remote source address of the datagram. May be an invalid address, theoretically. + * @param local_addr returns the local destination IP address. May be an invalid address. + * @return 1 on success, 0 on failure + */ +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); + +#ifndef BADVPN_USE_WINAPI +/** + * Returns the underlying socket file descriptor of the datagram object. + * Available on Unix-like systems only. + * + * @param o the object + * @return file descriptor + */ +int BDatagram_GetFd (BDatagram *o); +#endif + +/** + * Sets the SO_REUSEADDR option for the underlying socket. + * + * @param o the object + * @param reuse value of the option. Must be 0 or 1. + */ +int BDatagram_SetReuseAddr (BDatagram *o, int reuse); + +/** + * Initializes the send interface. + * The send interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_SendAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the send interface. + * The send interface must be initialized. + * If the send interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_SendAsync_Free (BDatagram *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_SendAsync_Init}. + * + * @param o the object + * @return send interface + */ +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o); + +/** + * Initializes the receive interface. + * The receive interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the receive interface. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_RecvAsync_Free (BDatagram *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_RecvAsync_Init}. + * + * @param o the object + * @return receive interface + */ +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o); + +#ifdef BADVPN_USE_WINAPI +#include "BDatagram_win.h" +#else +#include "BDatagram_unix.h" +#endif + +#endif diff --git a/app/src/main/jni/badvpn/system/BDatagram_unix.c b/app/src/main/jni/badvpn/system/BDatagram_unix.c new file mode 100644 index 0000000..67853db --- /dev/null +++ b/app/src/main/jni/badvpn/system/BDatagram_unix.c @@ -0,0 +1,855 @@ +/** + * @file BDatagram_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef BADVPN_LINUX +# include +# include +#endif + +#include +#include + +#include "BDatagram.h" + +#include + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +#ifdef BADVPN_LINUX + struct sockaddr_ll packet; +#endif + } addr; +}; + +static int family_socket_to_sys (int family); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void set_pktinfo (int fd, int family); +static void report_error (BDatagram *o); +static void do_send (BDatagram *o); +static void do_recv (BDatagram *o); +static void fd_handler (BDatagram *o, int events); +static void send_job_handler (BDatagram *o); +static void recv_job_handler (BDatagram *o); +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len); +static void recv_if_handler_recv (BDatagram *o, uint8_t *data); + +static int family_socket_to_sys (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + return AF_INET; + case BADDR_TYPE_IPV6: + return AF_INET6; +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + return AF_PACKET; +#endif + } + + ASSERT(0); + return 0; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: { + ASSERT(addr.packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + memset(&out->addr.packet, 0, sizeof(out->addr.packet)); + out->len = sizeof(out->addr.packet); + out->addr.packet.sll_family = AF_PACKET; + out->addr.packet.sll_protocol = addr.packet.phys_proto; + out->addr.packet.sll_ifindex = addr.packet.interface_index; + out->addr.packet.sll_hatype = 1; // linux/if_arp.h: #define ARPHRD_ETHER 1 + switch (addr.packet.packet_type) { + case BADDR_PACKET_PACKET_TYPE_HOST: + out->addr.packet.sll_pkttype = PACKET_HOST; + break; + case BADDR_PACKET_PACKET_TYPE_BROADCAST: + out->addr.packet.sll_pkttype = PACKET_BROADCAST; + break; + case BADDR_PACKET_PACKET_TYPE_MULTICAST: + out->addr.packet.sll_pkttype = PACKET_MULTICAST; + break; + case BADDR_PACKET_PACKET_TYPE_OTHERHOST: + out->addr.packet.sll_pkttype = PACKET_OTHERHOST; + break; + case BADDR_PACKET_PACKET_TYPE_OUTGOING: + out->addr.packet.sll_pkttype = PACKET_OUTGOING; + break; + default: + ASSERT(0); + } + out->addr.packet.sll_halen = 6; + memcpy(out->addr.packet.sll_addr, addr.packet.phys_addr, 6); + } break; +#endif + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + +#ifdef BADVPN_LINUX + case AF_PACKET: { + if (addr.len < offsetof(struct sockaddr_ll, sll_addr) + 6) { + goto fail; + } + if (addr.addr.packet.sll_hatype != 1) { // linux/if_arp.h: #define ARPHRD_ETHER 1 + goto fail; + } + int packet_type; + switch (addr.addr.packet.sll_pkttype) { + case PACKET_HOST: + packet_type = BADDR_PACKET_PACKET_TYPE_HOST; + break; + case PACKET_BROADCAST: + packet_type = BADDR_PACKET_PACKET_TYPE_BROADCAST; + break; + case PACKET_MULTICAST: + packet_type = BADDR_PACKET_PACKET_TYPE_MULTICAST; + break; + case PACKET_OTHERHOST: + packet_type = BADDR_PACKET_PACKET_TYPE_OTHERHOST; + break; + case PACKET_OUTGOING: + packet_type = BADDR_PACKET_PACKET_TYPE_OUTGOING; + break; + default: + goto fail; + } + if (addr.addr.packet.sll_halen != 6) { + goto fail; + } + BAddr_InitPacket(out, addr.addr.packet.sll_protocol, addr.addr.packet.sll_ifindex, BADDR_PACKET_HEADER_TYPE_ETHERNET, packet_type, addr.addr.packet.sll_addr); + } break; +#endif + + fail: + default: { + BAddr_InitNone(out); + } break; + } +} + +static void set_pktinfo (int fd, int family) +{ + int opt = 1; + + switch (family) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_RECVDSTADDR) failed"); + } +#else + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed"); + } +#endif + } break; + +#ifdef IPV6_RECVPKTINFO + case BADDR_TYPE_IPV6: { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IPV6_RECVPKTINFO) failed"); + } + } break; +#endif + } +} + +static void report_error (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR)); + return; +} + +static void do_send (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + // limit + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + // convert destination address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, o->send.remote_addr); + + struct iovec iov; + iov.iov_base = (uint8_t *)o->send.busy_data; + iov.iov_len = o->send.busy_data_len; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sysaddr.len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + size_t controllen = 0; + + switch (o->send.local_addr.type) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + addrinfo->s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_addr)); +#else + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + pktinfo->ipi_spec_dst.s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); +#endif + } break; + + case BADDR_TYPE_IPV6: { + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16); + controllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + } break; + } + + msg.msg_controllen = controllen; + + if (msg.msg_controllen == 0) { + msg.msg_control = NULL; + } + + // send + int bytes = sendmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->send.busy_data_len) + + if (bytes < o->send.busy_data_len) { + BLog(BLOG_ERROR, "send sent too little"); + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + // set not busy + o->send.busy = 0; + + // done + PacketPassInterface_Done(&o->send.iface); +} + +static void do_recv (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + // limit + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + struct sys_addr sysaddr; + + struct iovec iov; + iov.iov_base = o->recv.busy_data; + iov.iov_len = o->recv.mtu; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sizeof(sysaddr.addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + // recv + int bytes = recvmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->recv.mtu) + + // read returned address + sysaddr.len = msg.msg_namelen; + addr_sys_to_socket(&o->recv.remote_addr, sysaddr); + + // read returned local address + BIPAddr_InitInvalid(&o->recv.local_addr); + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { +#ifdef BADVPN_FREEBSD + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, addrinfo->s_addr); + } +#else + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr); + } +#endif + else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr); + } + } + + // set have addresses + o->recv.have_addrs = 1; + + // set not busy + o->recv.busy = 0; + + // done + PacketRecvInterface_Done(&o->recv.iface, bytes); +} + +static void fd_handler (BDatagram *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.inited && o->send.busy && o->send.have_addrs)) { + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.inited && o->recv.busy && o->recv.started)) { + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + do_send(o); + return; + } + + if (have_recv) { + do_recv(o); + return; + } + + BLog(BLOG_ERROR, "fd error event"); + report_error(o); + return; +} + +static void send_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + do_send(o); + return; +} + +static void recv_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + do_recv(o); + return; +} + +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(!o->send.busy) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->send.mtu) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.busy = 1; + + // if have no addresses, wait + if (!o->send.have_addrs) { + return; + } + + // set job + BPending_Set(&o->send.job); +} + +static void recv_if_handler_recv (BDatagram *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(!o->recv.busy) + + // remember data + o->recv.busy_data = data; + + // set busy + o->recv.busy = 1; + + // if recv not started yet, wait + if (!o->recv.started) { + return; + } + + // set job + BPending_Set(&o->recv.job); +} + +int BDatagram_AddressFamilySupported (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: +#endif + return 1; + } + + return 0; +} + +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) +{ + ASSERT(BDatagram_AddressFamilySupported(family)) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // init fd + if ((o->fd = socket(family_socket_to_sys(family), SOCK_DGRAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // enable receiving pktinfo + set_pktinfo(o->fd, family); + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BDATAGRAM_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BDATAGRAM_RECV_LIMIT); + + // set have no send and recv addresses + o->send.have_addrs = 0; + o->recv.have_addrs = 0; + + // set recv not started + o->recv.started = 0; + + // set send and recv not inited + o->send.inited = 0; + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void BDatagram_Free (BDatagram *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +int BDatagram_Bind (BDatagram *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // translate address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + return 0; + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + return 1; +} + +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(remote_addr.type)) + ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type)) + + // set addresses + o->send.remote_addr = remote_addr; + o->send.local_addr = local_addr; + + if (!o->send.have_addrs) { + // set have addresses + o->send.have_addrs = 1; + + // start sending + if (o->send.inited && o->send.busy) { + BPending_Set(&o->send.job); + } + } +} + +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + if (!o->recv.have_addrs) { + return 0; + } + + *remote_addr = o->recv.remote_addr; + *local_addr = o->recv.local_addr; + return 1; +} + +int BDatagram_GetFd (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + + return o->fd; +} + +int BDatagram_SetReuseAddr (BDatagram *o, int reuse) +{ + DebugObject_Access(&o->d_obj); + ASSERT(reuse == 0 || reuse == 1) + + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { + return 0; + } + + return 1; +} + +void BDatagram_SendAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->send.inited) + ASSERT(mtu >= 0) + + // init arguments + o->send.mtu = mtu; + + // init interface + PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o); + + // set not busy + o->send.busy = 0; + + // set inited + o->send.inited = 1; +} + +void BDatagram_SendAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // update events + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->send.job); + + // free interface + PacketPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(mtu >= 0) + + // init arguments + o->recv.mtu = mtu; + + // init interface + PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o); + + // set not busy + o->recv.busy = 0; + + // set inited + o->recv.inited = 1; +} + +void BDatagram_RecvAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // update events + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->recv.job); + + // free interface + PacketRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/app/src/main/jni/badvpn/system/BDatagram_unix.h b/app/src/main/jni/badvpn/system/BDatagram_unix.h new file mode 100644 index 0000000..3ef0262 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BDatagram_unix.h @@ -0,0 +1,71 @@ +/** + * @file BDatagram_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BDATAGRAM_SEND_LIMIT 2 +#define BDATAGRAM_RECV_LIMIT 2 + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + int fd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int busy; + const uint8_t *busy_data; + int busy_data_len; + } send; + struct { + BReactorLimit limit; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int busy; + uint8_t *busy_data; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/app/src/main/jni/badvpn/system/BDatagram_win.c b/app/src/main/jni/badvpn/system/BDatagram_win.c new file mode 100644 index 0000000..0528866 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BDatagram_win.c @@ -0,0 +1,755 @@ +/** + * @file BDatagram_win.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "BDatagram.h" + +#include + +static int family_socket_to_sys (int family); +static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr); +static void set_pktinfo (SOCKET sock, int family); +static void report_error (BDatagram *o); +static void datagram_abort (BDatagram *o); +static void start_send (BDatagram *o); +static void start_recv (BDatagram *o); +static void send_job_handler (BDatagram *o); +static void recv_job_handler (BDatagram *o); +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len); +static void recv_if_handler_recv (BDatagram *o, uint8_t *data); +static void send_olap_handler (BDatagram *o, int event, DWORD bytes); +static void recv_olap_handler (BDatagram *o, int event, DWORD bytes); + +static int family_socket_to_sys (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + return AF_INET; + case BADDR_TYPE_IPV6: + return AF_INET6; + } + + ASSERT(0); + return 0; +} + +static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void set_pktinfo (SOCKET sock, int family) +{ + DWORD opt = 1; + + switch (family) { + case BADDR_TYPE_IPV4: { + if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed"); + } + } break; + + case BADDR_TYPE_IPV6: { + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IPV6_PKTINFO) failed"); + } + } break; + } +} + +static void report_error (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR)); + return; +} + +static void datagram_abort (BDatagram *o) +{ + ASSERT(!o->aborted) + + // cancel I/O + if ((o->recv.inited && o->recv.data_have && o->recv.data_busy) || (o->send.inited && o->send.data_len >= 0 && o->send.data_busy)) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for receiving to complete + if (o->recv.inited && o->recv.data_have && o->recv.data_busy) { + BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL); + } + + // wait for sending to complete + if (o->send.inited && o->send.data_len >= 0 && o->send.data_busy) { + BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv.olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send.olap); + + // set aborted + o->aborted = 1; +} + +static void start_send (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(!o->send.data_busy) + ASSERT(o->send.have_addrs) + + // convert destination address + addr_socket_to_sys(&o->send.sysaddr, o->send.remote_addr); + + WSABUF buf; + buf.buf = (char *)o->send.data; + buf.len = (o->send.data_len > ULONG_MAX ? ULONG_MAX : o->send.data_len); + + memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap)); + + if (o->fnWSASendMsg) { + o->send.msg.name = &o->send.sysaddr.addr.generic; + o->send.msg.namelen = o->send.sysaddr.len; + o->send.msg.lpBuffers = &buf; + o->send.msg.dwBufferCount = 1; + o->send.msg.Control.buf = (char *)&o->send.cdata; + o->send.msg.Control.len = sizeof(o->send.cdata); + o->send.msg.dwFlags = 0; + + int sum = 0; + + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->send.msg); + + switch (o->send.local_addr.type) { + case BADDR_TYPE_IPV4: { + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg); + pktinfo->ipi_addr.s_addr = o->send.local_addr.ipv4; + sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo)); + } break; + case BADDR_TYPE_IPV6: { + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg); + memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16); + sum += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)); + } break; + } + + o->send.msg.Control.len = sum; + + if (o->send.msg.Control.len == 0) { + o->send.msg.Control.buf = NULL; + } + + // send + int res = o->fnWSASendMsg(o->sock, &o->send.msg, 0, NULL, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASendMsg failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } else { + // send + int res = WSASendTo(o->sock, &buf, 1, NULL, 0, &o->send.sysaddr.addr.generic, o->send.sysaddr.len, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASendTo failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } + + // set busy + o->send.data_busy = 1; +} + +static void start_recv (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(!o->recv.data_busy) + ASSERT(o->recv.started) + + WSABUF buf; + buf.buf = (char *)o->recv.data; + buf.len = (o->recv.mtu > ULONG_MAX ? ULONG_MAX : o->recv.mtu); + + memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap)); + + if (o->fnWSARecvMsg) { + o->recv.msg.name = &o->recv.sysaddr.addr.generic; + o->recv.msg.namelen = sizeof(o->recv.sysaddr.addr); + o->recv.msg.lpBuffers = &buf; + o->recv.msg.dwBufferCount = 1; + o->recv.msg.Control.buf = (char *)&o->recv.cdata; + o->recv.msg.Control.len = sizeof(o->recv.cdata); + o->recv.msg.dwFlags = 0; + + // recv + int res = o->fnWSARecvMsg(o->sock, &o->recv.msg, NULL, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecvMsg failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } else { + o->recv.sysaddr.len = sizeof(o->recv.sysaddr.addr); + + // recv + DWORD flags = 0; + int res = WSARecvFrom(o->sock, &buf, 1, NULL, &flags, &o->recv.sysaddr.addr.generic, &o->recv.sysaddr.len, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecvFrom failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } + + // set busy + o->recv.data_busy = 1; +} + +static void send_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(!o->send.data_busy) + ASSERT(o->send.have_addrs) + + // send + start_send(o); + return; +} + +static void recv_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(!o->recv.data_busy) + ASSERT(o->recv.started) + + // recv + start_recv(o); + return; +} + +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len == -1) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->send.mtu) + + // remember data + o->send.data = data; + o->send.data_len = data_len; + o->send.data_busy = 0; + + // if have no addresses, wait + if (!o->send.have_addrs) { + return; + } + + // send + start_send(o); + return; +} + +static void recv_if_handler_recv (BDatagram *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(!o->recv.data_have) + + // remember data + o->recv.data = data; + o->recv.data_have = 1; + o->recv.data_busy = 0; + + // if recv not started yet, wait + if (!o->recv.started) { + return; + } + + // recv + start_recv(o); + return; +} + +static void send_olap_handler (BDatagram *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(o->send.data_busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->send.data_busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "sending failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->send.data_len) + + if (bytes < o->send.data_len) { + BLog(BLOG_ERROR, "sent too little"); + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.data_have) { + ASSERT(!o->recv.data_busy) + + BPending_Set(&o->recv.job); + } + } + + // set no data + o->send.data_len = -1; + + // done + PacketPassInterface_Done(&o->send.iface); +} + +static void recv_olap_handler (BDatagram *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(o->recv.data_busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->recv.data_busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "receiving failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->recv.mtu) + + if (o->fnWSARecvMsg) { + o->recv.sysaddr.len = o->recv.msg.namelen; + } + + // read remote address + addr_sys_to_socket(&o->recv.remote_addr, o->recv.sysaddr); + + // read local address + BIPAddr_InitInvalid(&o->recv.local_addr); + if (o->fnWSARecvMsg) { + for (WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->recv.msg); cmsg; cmsg = WSA_CMSG_NXTHDR(&o->recv.msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr); + } + else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg); + BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr); + } + } + } + + // set have addresses + o->recv.have_addrs = 1; + + // set no data + o->recv.data_have = 0; + + // done + PacketRecvInterface_Done(&o->recv.iface, bytes); +} + +int BDatagram_AddressFamilySupported (int family) +{ + return (family == BADDR_TYPE_IPV4 || family == BADDR_TYPE_IPV6); +} + +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) +{ + ASSERT(BDatagram_AddressFamilySupported(family)) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // init socket + if ((o->sock = WSASocket(family_socket_to_sys(family), SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + DWORD out_bytes; + + // obtain WSASendMsg + GUID guid1 = WSAID_WSASENDMSG; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid1, sizeof(guid1), &o->fnWSASendMsg, sizeof(o->fnWSASendMsg), &out_bytes, NULL, NULL) != 0) { + o->fnWSASendMsg = NULL; + } + + // obtain WSARecvMsg + GUID guid2 = WSAID_WSARECVMSG; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid2, sizeof(guid2), &o->fnWSARecvMsg, sizeof(o->fnWSARecvMsg), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "failed to obtain WSARecvMsg"); + o->fnWSARecvMsg = NULL; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // enable receiving pktinfo + set_pktinfo(o->sock, family); + + // set not aborted + o->aborted = 0; + + // init send olap + BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)send_olap_handler); + + // set have no send addrs + o->send.have_addrs = 0; + + // set send not inited + o->send.inited = 0; + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler); + + // set recv not started + o->recv.started = 0; + + // set have no recv addrs + o->recv.have_addrs = 0; + + // set recv not inited + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BDatagram_Free (BDatagram *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + if (!o->aborted) { + datagram_abort(o); + } +} + +int BDatagram_Bind (BDatagram *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // translate address + struct BDatagram_sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // bind + if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + return 0; + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.data_have) { + ASSERT(!o->recv.data_busy) + + BPending_Set(&o->recv.job); + } + } + + return 1; +} + +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(BDatagram_AddressFamilySupported(remote_addr.type)) + ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type)) + + // set addresses + o->send.remote_addr = remote_addr; + o->send.local_addr = local_addr; + + // set have addresses + o->send.have_addrs = 1; + + // start sending + if (o->send.inited && o->send.data_len >= 0 && !o->send.data_busy) { + BPending_Set(&o->send.job); + } +} + +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + if (!o->recv.have_addrs) { + return 0; + } + + *remote_addr = o->recv.remote_addr; + *local_addr = o->recv.local_addr; + return 1; +} + +int BDatagram_SetReuseAddr (BDatagram *o, int reuse) +{ + DebugObject_Access(&o->d_obj); + ASSERT(reuse == 0 || reuse == 1) + + if (setsockopt(o->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { + return 0; + } + + return 1; +} + +void BDatagram_SendAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->send.inited) + ASSERT(mtu >= 0) + + // init arguments + o->send.mtu = mtu; + + // init interface + PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o); + + // set have no data + o->send.data_len = -1; + + // set inited + o->send.inited = 1; +} + +void BDatagram_SendAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // abort if busy + if (o->send.data_len >= 0 && o->send.data_busy && !o->aborted) { + datagram_abort(o); + } + + // free job + BPending_Free(&o->send.job); + + // free interface + PacketPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->recv.inited) + ASSERT(mtu >= 0) + + // init arguments + o->recv.mtu = mtu; + + // init interface + PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o); + + // set have no data + o->recv.data_have = 0; + + // set inited + o->recv.inited = 1; +} + +void BDatagram_RecvAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // abort if busy + if (o->recv.data_have && o->recv.data_busy && !o->aborted) { + datagram_abort(o); + } + + // free job + BPending_Free(&o->recv.job); + + // free interface + PacketRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/app/src/main/jni/badvpn/system/BDatagram_win.h b/app/src/main/jni/badvpn/system/BDatagram_win.h new file mode 100644 index 0000000..9831946 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BDatagram_win.h @@ -0,0 +1,99 @@ +/** + * @file BDatagram_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BDatagram_sys_addr { + int len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + SOCKET sock; + LPFN_WSASENDMSG fnWSASendMsg; + LPFN_WSARECVMSG fnWSARecvMsg; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int data_len; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } send; + struct { + BReactorIOCPOverlapped olap; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int data_have; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/app/src/main/jni/badvpn/system/BInputProcess.c b/app/src/main/jni/badvpn/system/BInputProcess.c new file mode 100644 index 0000000..b3d096a --- /dev/null +++ b/app/src/main/jni/badvpn/system/BInputProcess.c @@ -0,0 +1,211 @@ +/** + * @file BInputProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "BInputProcess.h" + +#include + +static void connection_handler (BInputProcess *o, int event); +static void process_handler (BInputProcess *o, int normally, uint8_t normally_exit_status); + +void connection_handler (BInputProcess *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pipe_fd >= 0) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "pipe closed"); + } else { + BLog(BLOG_ERROR, "pipe error"); + } + + // free pipe connection read interface + BConnection_RecvAsync_Free(&o->pipe_con); + + // free pipe connection + BConnection_Free(&o->pipe_con); + + // close pipe read end + ASSERT_FORCE(close(o->pipe_fd) == 0) + + // forget pipe + o->pipe_fd = -1; + + // call closed handler + o->handler_closed(o->user, (event != BCONNECTION_EVENT_RECVCLOSED)); + return; +} + +void process_handler (BInputProcess *o, int normally, uint8_t normally_exit_status) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + // free process + BProcess_Free(&o->process); + + // set not have process + o->have_process = 0; + + // call terminated handler + o->handler_terminated(o->user, normally, normally_exit_status); + return; +} + +int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *manager, void *user, + BInputProcess_handler_terminated handler_terminated, + BInputProcess_handler_closed handler_closed) +{ + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->manager = manager; + o->user = user; + o->handler_terminated = handler_terminated; + o->handler_closed = handler_closed; + + // create pipe + int pipefds[2]; + if (pipe(pipefds) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail0; + } + + // init pipe connection + if (!BConnection_Init(&o->pipe_con, BConnection_source_pipe(pipefds[0]), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init pipe connection read interface + BConnection_RecvAsync_Init(&o->pipe_con); + + // remember pipe fds + o->pipe_fd = pipefds[0]; + o->pipe_write_fd = pipefds[1]; + + // set not started + o->started = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + ASSERT_FORCE(close(pipefds[0]) == 0) + ASSERT_FORCE(close(pipefds[1]) == 0) +fail0: + return 0; +} + +void BInputProcess_Free (BInputProcess *o) +{ + DebugObject_Free(&o->d_obj); + + if (!o->started) { + // close pipe write end + ASSERT_FORCE(close(o->pipe_write_fd) == 0) + } else { + // free process + if (o->have_process) { + BProcess_Free(&o->process); + } + } + + if (o->pipe_fd >= 0) { + // free pipe connection read interface + BConnection_RecvAsync_Free(&o->pipe_con); + + // free pipe connection + BConnection_Free(&o->pipe_con); + + // close pipe read end + ASSERT_FORCE(close(o->pipe_fd) == 0) + } +} + +int BInputProcess_Start (BInputProcess *o, const char *file, char *const argv[], const char *username) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->started) + + // start process + int fds[] = { o->pipe_write_fd, -1 }; + int fds_map[] = { 1 }; + if (!BProcess_InitWithFds(&o->process, o->manager, (BProcess_handler)process_handler, o, file, argv, username, fds, fds_map)) { + BLog(BLOG_ERROR, "BProcess_Init failed"); + goto fail0; + } + + // close pipe write end + ASSERT_FORCE(close(o->pipe_write_fd) == 0) + + // set started + o->started = 1; + + // set have process + o->have_process = 1; + + return 1; + +fail0: + return 0; +} + +int BInputProcess_Terminate (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + return BProcess_Terminate(&o->process); +} + +int BInputProcess_Kill (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + return BProcess_Kill(&o->process); +} + +StreamRecvInterface * BInputProcess_GetInput (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pipe_fd >= 0) + + return BConnection_RecvAsync_GetIf(&o->pipe_con); +} diff --git a/app/src/main/jni/badvpn/system/BInputProcess.h b/app/src/main/jni/badvpn/system/BInputProcess.h new file mode 100644 index 0000000..7317a5d --- /dev/null +++ b/app/src/main/jni/badvpn/system/BInputProcess.h @@ -0,0 +1,65 @@ +/** + * @file BInputProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BINPUTPROCESS_H +#define BADVPN_BINPUTPROCESS_H + +#include +#include +#include +#include + +typedef void (*BInputProcess_handler_terminated) (void *user, int normally, uint8_t normally_exit_status); +typedef void (*BInputProcess_handler_closed) (void *user, int is_error); + +typedef struct { + BReactor *reactor; + BProcessManager *manager; + void *user; + BInputProcess_handler_terminated handler_terminated; + BInputProcess_handler_closed handler_closed; + int pipe_write_fd; + int started; + int have_process; + BProcess process; + int pipe_fd; + BConnection pipe_con; + DebugObject d_obj; +} BInputProcess; + +int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *manager, void *user, + BInputProcess_handler_terminated handler_terminated, + BInputProcess_handler_closed handler_closed) WARN_UNUSED; +void BInputProcess_Free (BInputProcess *o); +int BInputProcess_Start (BInputProcess *o, const char *file, char *const argv[], const char *username); +int BInputProcess_Terminate (BInputProcess *o); +int BInputProcess_Kill (BInputProcess *o); +StreamRecvInterface * BInputProcess_GetInput (BInputProcess *o); + +#endif diff --git a/app/src/main/jni/badvpn/system/BLockReactor.c b/app/src/main/jni/badvpn/system/BLockReactor.c new file mode 100644 index 0000000..e9a2724 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BLockReactor.c @@ -0,0 +1,131 @@ +/** + * @file BLockReactor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "BLockReactor.h" + +#include + +static void thread_signal_handler (BThreadSignal *thread_signal) +{ + BLockReactor *o = UPPER_OBJECT(thread_signal, BLockReactor, thread_signal); + DebugObject_Access(&o->d_obj); + + ASSERT_FORCE(sem_post(&o->sem1) == 0) + ASSERT_FORCE(sem_wait(&o->sem2) == 0) +} + +int BLockReactor_Init (BLockReactor *o, BReactor *reactor) +{ + o->reactor = reactor; + + if (!BThreadSignal_Init(&o->thread_signal, reactor, thread_signal_handler)) { + BLog(BLOG_ERROR, "BThreadSignal_Init failed"); + goto fail0; + } + + if (sem_init(&o->sem1, 0, 0) < 0) { + BLog(BLOG_ERROR, "sem_init failed"); + goto fail1; + } + + if (sem_init(&o->sem2, 0, 0) < 0) { + BLog(BLOG_ERROR, "sem_init failed"); + goto fail2; + } + +#ifndef NDEBUG + o->locked = 0; +#endif + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (sem_close(&o->sem1) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } +fail1: + BThreadSignal_Free(&o->thread_signal); +fail0: + return 0; +} + +void BLockReactor_Free (BLockReactor *o) +{ + DebugObject_Free(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->locked) +#endif + + if (sem_destroy(&o->sem2) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } + if (sem_destroy(&o->sem1) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } + BThreadSignal_Free(&o->thread_signal); +} + +int BLockReactor_Thread_Lock (BLockReactor *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->locked) +#endif + + if (!BThreadSignal_Thread_Signal(&o->thread_signal)) { + return 0; + } + + ASSERT_FORCE(sem_wait(&o->sem1) == 0) + +#ifndef NDEBUG + o->locked = 1; +#endif + + return 1; +} + +void BLockReactor_Thread_Unlock (BLockReactor *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(o->locked) +#endif + +#ifndef NDEBUG + o->locked = 0; +#endif + + ASSERT_FORCE(sem_post(&o->sem2) == 0) +} diff --git a/app/src/main/jni/badvpn/system/BLockReactor.h b/app/src/main/jni/badvpn/system/BLockReactor.h new file mode 100644 index 0000000..84acab8 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BLockReactor.h @@ -0,0 +1,58 @@ +/** + * @file BLockReactor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_LOCK_REACTOR_H +#define BADVPN_B_LOCK_REACTOR_H + +#include + +#include +#include +#include +#include + +typedef struct BLockReactor_s BLockReactor; + +struct BLockReactor_s { + BReactor *reactor; + BThreadSignal thread_signal; + sem_t sem1; + sem_t sem2; +#ifndef NDEBUG + int locked; +#endif + DebugObject d_obj; +}; + +int BLockReactor_Init (BLockReactor *o, BReactor *reactor) WARN_UNUSED; +void BLockReactor_Free (BLockReactor *o); +int BLockReactor_Thread_Lock (BLockReactor *o); +void BLockReactor_Thread_Unlock (BLockReactor *o); + +#endif diff --git a/app/src/main/jni/badvpn/system/BNetwork.c b/app/src/main/jni/badvpn/system/BNetwork.c new file mode 100644 index 0000000..b48ae61 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BNetwork.c @@ -0,0 +1,99 @@ +/** + * @file BNetwork.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +extern int bnetwork_initialized; + +#ifndef BADVPN_PLUGIN +int bnetwork_initialized = 0; +#endif + +int BNetwork_GlobalInit (void) +{ + ASSERT(!bnetwork_initialized) + +#ifdef BADVPN_USE_WINAPI + + WORD requested = MAKEWORD(2, 2); + WSADATA wsadata; + if (WSAStartup(requested, &wsadata) != 0) { + BLog(BLOG_ERROR, "WSAStartup failed"); + goto fail0; + } + if (wsadata.wVersion != requested) { + BLog(BLOG_ERROR, "WSAStartup returned wrong version"); + goto fail1; + } + +#else + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGPIPE, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto fail0; + } + +#endif + + bnetwork_initialized = 1; + + return 1; + +#ifdef BADVPN_USE_WINAPI +fail1: + WSACleanup(); +#endif + +fail0: + return 0; +} + +void BNetwork_Assert (void) +{ + ASSERT(bnetwork_initialized) +} diff --git a/app/src/main/jni/badvpn/system/BNetwork.h b/app/src/main/jni/badvpn/system/BNetwork.h new file mode 100644 index 0000000..0db1744 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BNetwork.h @@ -0,0 +1,36 @@ +/** + * @file BNetwork.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BNETWORK_H +#define BADVPN_SYSTEM_BNETWORK_H + +int BNetwork_GlobalInit (void); +void BNetwork_Assert (void); + +#endif diff --git a/app/src/main/jni/badvpn/system/BProcess.c b/app/src/main/jni/badvpn/system/BProcess.c new file mode 100644 index 0000000..7efea25 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BProcess.c @@ -0,0 +1,400 @@ +/** + * @file BProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "BProcess.h" + +#include + +static void call_handler (BProcess *o, int normally, uint8_t normally_exit_status) +{ + DEBUGERROR(&o->d_err, o->handler(o->user, normally, normally_exit_status)) +} + +static BProcess * find_process (BProcessManager *o, pid_t pid) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->processes); node; node = LinkedList1Node_Next(node)) { + BProcess *p = UPPER_OBJECT(node, BProcess, list_node); + if (p->pid == pid) { + return p; + } + } + + return NULL; +} + +static void work_signals (BProcessManager *o) +{ + // read exit status with waitpid() + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) { + return; + } + + // schedule next waitpid + BPending_Set(&o->wait_job); + + // find process + BProcess *p = find_process(o, pid); + if (!p) { + BLog(BLOG_DEBUG, "unknown child %p"); + } + + if (WIFEXITED(status)) { + uint8_t exit_status = WEXITSTATUS(status); + + BLog(BLOG_INFO, "child %"PRIiMAX" exited with status %"PRIu8, (intmax_t)pid, exit_status); + + if (p) { + call_handler(p, 1, exit_status); + return; + } + } + else if (WIFSIGNALED(status)) { + int signo = WTERMSIG(status); + + BLog(BLOG_INFO, "child %"PRIiMAX" exited with signal %d", (intmax_t)pid, signo); + + if (p) { + call_handler(p, 0, 0); + return; + } + } + else { + BLog(BLOG_ERROR, "unknown wait status type for pid %"PRIiMAX" (%d)", (intmax_t)pid, status); + } +} + +static void wait_job_handler (BProcessManager *o) +{ + DebugObject_Access(&o->d_obj); + + work_signals(o); + return; +} + +static void signal_handler (BProcessManager *o, int signo) +{ + ASSERT(signo == SIGCHLD) + DebugObject_Access(&o->d_obj); + + work_signals(o); + return; +} + +int BProcessManager_Init (BProcessManager *o, BReactor *reactor) +{ + // init arguments + o->reactor = reactor; + + // init signal handling + sigset_t sset; + ASSERT_FORCE(sigemptyset(&sset) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGCHLD) == 0) + if (!BUnixSignal_Init(&o->signal, o->reactor, sset, (BUnixSignal_handler)signal_handler, o)) { + BLog(BLOG_ERROR, "BUnixSignal_Init failed"); + goto fail0; + } + + // init processes list + LinkedList1_Init(&o->processes); + + // init wait job + BPending_Init(&o->wait_job, BReactor_PendingGroup(o->reactor), (BPending_handler)wait_job_handler, o); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void BProcessManager_Free (BProcessManager *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->processes)) + DebugObject_Free(&o->d_obj); + + // free wait job + BPending_Free(&o->wait_job); + + // free signal handling + BUnixSignal_Free(&o->signal, 1); +} + +static int fds_contains (const int *fds, int fd, size_t *pos) +{ + for (size_t i = 0; fds[i] >= 0; i++) { + if (fds[i] == fd) { + if (pos) { + *pos = i; + } + + return 1; + } + } + + return 0; +} + +int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params) +{ + // init arguments + o->m = m; + o->handler = handler; + o->user = user; + + // count fds + size_t num_fds; + for (num_fds = 0; params.fds[num_fds] >= 0; num_fds++); + + // block signals + // needed to prevent parent's signal handlers from being called + // in the child + sigset_t sset_all; + sigfillset(&sset_all); + sigset_t sset_old; + if (sigprocmask(SIG_SETMASK, &sset_all, &sset_old) < 0) { + BLog(BLOG_ERROR, "sigprocmask failed"); + goto fail0; + } + + // fork + pid_t pid = fork(); + + if (pid == 0) { + // this is child + + // restore signal dispositions + for (int i = 1; i < NSIG; i++) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigaction(i, &sa, NULL); + } + + // unblock signals + sigset_t sset_none; + sigemptyset(&sset_none); + if (sigprocmask(SIG_SETMASK, &sset_none, NULL) < 0) { + abort(); + } + + // copy fds array + int *fds2 = malloc((num_fds + 1) * sizeof(fds2[0])); + if (!fds2) { + abort(); + } + memcpy(fds2, params.fds, (num_fds + 1) * sizeof(fds2[0])); + + // find maximum file descriptors + int max_fd = sysconf(_SC_OPEN_MAX); + if (max_fd < 0) { + abort(); + } + + // close file descriptors + for (int i = 0; i < max_fd; i++) { + // if it's one of the given fds, don't close it + if (fds_contains(fds2, i, NULL)) { + continue; + } + + close(i); + } + + // map fds to requested fd numbers + while (*fds2 >= 0) { + // resolve possible conflict + size_t cpos; + if (fds_contains(fds2 + 1, *params.fds_map, &cpos)) { + // dup() the fd to a new number; the old one will be closed + // in the following dup2() + if ((fds2[1 + cpos] = dup(fds2[1 + cpos])) < 0) { + abort(); + } + } + + if (*fds2 != *params.fds_map) { + // dup fd + if (dup2(*fds2, *params.fds_map) < 0) { + abort(); + } + + // close original fd + close(*fds2); + } + + fds2++; + params.fds_map++; + } + + // make sure standard streams are open + open_standard_streams(); + + // make session leader if requested + if (params.do_setsid) { + setsid(); + } + + // assume identity of username, if requested + if (params.username) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + abort(); + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(params.username, &pwd, buf, bufsize, &res); + if (!res) { + abort(); + } + + if (initgroups(params.username, pwd.pw_gid) < 0) { + abort(); + } + + if (setgid(pwd.pw_gid) < 0) { + abort(); + } + + if (setuid(pwd.pw_uid) < 0) { + abort(); + } + } + + execv(file, argv); + + abort(); + } + + // restore original signal mask + ASSERT_FORCE(sigprocmask(SIG_SETMASK, &sset_old, NULL) == 0) + + if (pid < 0) { + BLog(BLOG_ERROR, "fork failed"); + goto fail0; + } + + // remember pid + o->pid = pid; + + // add to processes list + LinkedList1_Append(&o->m->processes, &o->list_node); + + DebugObject_Init(&o->d_obj); + DebugError_Init(&o->d_err, BReactor_PendingGroup(m->reactor)); + + return 1; + +fail0: + return 0; +} + +int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map) +{ + struct BProcess_params params; + params.username = username; + params.fds = fds; + params.fds_map = fds_map; + params.do_setsid = 0; + + return BProcess_Init2(o, m, handler, user, file, argv, params); +} + +int BProcess_Init (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username) +{ + int fds[] = {-1}; + + return BProcess_InitWithFds(o, m, handler, user, file, argv, username, fds, NULL); +} + +void BProcess_Free (BProcess *o) +{ + DebugError_Free(&o->d_err); + DebugObject_Free(&o->d_obj); + + // remove from processes list + LinkedList1_Remove(&o->m->processes, &o->list_node); +} + +int BProcess_Terminate (BProcess *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + ASSERT(o->pid > 0) + + if (kill(o->pid, SIGTERM) < 0) { + BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGTERM) failed", (intmax_t)o->pid); + return 0; + } + + return 1; +} + +int BProcess_Kill (BProcess *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + ASSERT(o->pid > 0) + + if (kill(o->pid, SIGKILL) < 0) { + BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGKILL) failed", (intmax_t)o->pid); + return 0; + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/system/BProcess.h b/app/src/main/jni/badvpn/system/BProcess.h new file mode 100644 index 0000000..35993fe --- /dev/null +++ b/app/src/main/jni/badvpn/system/BProcess.h @@ -0,0 +1,200 @@ +/** + * @file BProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BPROCESS_H +#define BADVPN_BPROCESS_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Manages child processes. + * There may be at most one process manager at any given time. This restriction is not + * enforced, however. + */ +typedef struct { + BReactor *reactor; + BUnixSignal signal; + LinkedList1 processes; + BPending wait_job; + DebugObject d_obj; +} BProcessManager; + +/** + * Handler called when the process terminates. + * The process object must be freed from the job context of this handler. + * {@link BProcess_Terminate} or {@link BProcess_Kill} must not be called + * after this handler is called. + * + * @param user as in {@link BProcess_InitWithFds} or {@link BProcess_Init} + * @param normally whether the child process terminated normally (0 or 1) + * @param normally_exit_status if the child process terminated normally, its exit + * status; otherwise undefined + */ +typedef void (*BProcess_handler) (void *user, int normally, uint8_t normally_exit_status); + +/** + * Represents a child process. + */ +typedef struct { + BProcessManager *m; + BProcess_handler handler; + void *user; + pid_t pid; + LinkedList1Node list_node; // node in BProcessManager.processes + DebugObject d_obj; + DebugError d_err; +} BProcess; + +/** + * Initializes the process manager. + * There may be at most one process manager at any given time. This restriction is not + * enforced, however. + * + * @param o the object + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int BProcessManager_Init (BProcessManager *o, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the process manager. + * There must be no {@link BProcess} objects using this process manager. + * + * @param o the object + */ +void BProcessManager_Free (BProcessManager *o); + +struct BProcess_params { + const char *username; + const int *fds; + const int *fds_map; + int do_setsid; +}; + +/** + * Initializes the process. + * 'file', 'argv', 'username', 'fds' and 'fds_map' arguments are only used during this + * function call. + * If no file descriptor is mapped to a standard stream (file descriptors 0, 1, 2), + * then /dev/null will be opened in the child for that standard stream. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param params.username user account to run the program as, or NULL to not switch user + * @param params.fds array of file descriptors in the parent to map to file descriptors in the child, + * terminated with -1 + * @param params.fds_map array of file descriptors in the child that file descriptors in 'fds' will + * be mapped to, in the same order. Must contain the same number of file descriptors + * as the 'fds' argument, and does not have to be terminated with -1. + * @param params.do_setsid if set to non-zero, will make the child call setsid() before exec'ing. + * Failure of setsid() will be ignored. + * @return 1 on success, 0 on failure + */ +int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params) WARN_UNUSED; + +/** + * Initializes the process. + * 'file', 'argv', 'username', 'fds' and 'fds_map' arguments are only used during this + * function call. + * If no file descriptor is mapped to a standard stream (file descriptors 0, 1, 2), + * then /dev/null will be opened in the child for that standard stream. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param username user account to run the program as, or NULL to not switch user + * @param fds array of file descriptors in the parent to map to file descriptors in the child, + * terminated with -1 + * @param fds_map array of file descriptors in the child that file descriptors in 'fds' will + * be mapped to, in the same order. Must contain the same number of file descriptors + * as the 'fds' argument, and does not have to be terminated with -1. + * @return 1 on success, 0 on failure + */ +int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map) WARN_UNUSED; + +/** + * Initializes the process. + * Like {@link BProcess_InitWithFds}, but without file descriptor mapping. + * 'file', 'argv' and 'username' arguments are only used during this function call. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param username user account to run the program as, or NULL to not switch user + * @return 1 on success, 0 on failure + */ +int BProcess_Init (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username) WARN_UNUSED; + +/** + * Frees the process. + * This does not do anything with the actual child process; it only prevents the user to wait + * for its termination. If the process terminates while a process manager is running, it will still + * be waited for (and will not become a zombie). + * + * @param o the object + */ +void BProcess_Free (BProcess *o); + +/** + * Sends the process the SIGTERM signal. + * Success of this action does NOT mean that the child has terminated. + * + * @param o the object + * @return 1 on success, 0 on failure + */ +int BProcess_Terminate (BProcess *o); + +/** + * Sends the process the SIGKILL signal. + * Success of this action does NOT mean that the child has terminated. + * + * @param o the object + * @return 1 on success, 0 on failure + */ +int BProcess_Kill (BProcess *o); + +#endif diff --git a/app/src/main/jni/badvpn/system/BReactor.h b/app/src/main/jni/badvpn/system/BReactor.h new file mode 100644 index 0000000..4c0fa5b --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor.h @@ -0,0 +1,11 @@ +#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) + defined(BADVPN_BREACTOR_EMSCRIPTEN) != 1 +#error No reactor backend or too many reactor backens +#endif + +#if defined(BADVPN_BREACTOR_BADVPN) +#include "BReactor_badvpn.h" +#elif defined(BADVPN_BREACTOR_GLIB) +#include "BReactor_glib.h" +#elif defined(BADVPN_BREACTOR_EMSCRIPTEN) +#include "BReactor_emscripten.h" +#endif diff --git a/app/src/main/jni/badvpn/system/BReactor_badvpn.c b/app/src/main/jni/badvpn/system/BReactor_badvpn.c new file mode 100644 index 0000000..1b03d58 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_badvpn.c @@ -0,0 +1,1430 @@ +/** + * @file BReactor_badvpn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#define KEVENT_TAG_FD 1 +#define KEVENT_TAG_KEVENT 2 + +#define TIMER_STATE_INACTIVE 1 +#define TIMER_STATE_RUNNING 2 +#define TIMER_STATE_EXPIRED 3 + +static int compare_timers (BSmallTimer *t1, BSmallTimer *t2) +{ + int cmp = B_COMPARE(t1->absTime, t2->absTime); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)t1, (uintptr_t)t2); +} + +#include "BReactor_badvpn_timerstree.h" +#include + +static void assert_timer (BSmallTimer *bt) +{ + ASSERT(bt->state == TIMER_STATE_INACTIVE || bt->state == TIMER_STATE_RUNNING || + bt->state == TIMER_STATE_EXPIRED) +} + +static int move_expired_timers (BReactor *bsys, btime_t now) +{ + int moved = 0; + + // move timed out timers to the expired list + BReactor__TimersTreeRef ref; + BSmallTimer *timer; + while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + + // if it's in the future, stop + if (timer->absTime > now) { + break; + } + moved = 1; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } + + return moved; +} + +static void move_first_timers (BReactor *bsys) +{ + BReactor__TimersTreeRef ref; + + // get the time of the first timer + BSmallTimer *first_timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link; + ASSERT(first_timer) + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + btime_t first_time = first_timer->absTime; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &first_timer->u.list_node); + + // set expired + first_timer->state = TIMER_STATE_EXPIRED; + + // also move other timers with the same timeout + BSmallTimer *timer; + while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + ASSERT(timer->absTime >= first_time) + + // if it's in the future, stop + if (timer->absTime > first_time) { + break; + } + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } +} + +#ifdef BADVPN_USE_WINAPI + +static void set_iocp_ready (BReactorIOCPOverlapped *olap, int succeeded, DWORD bytes) +{ + BReactor *reactor = olap->reactor; + ASSERT(!olap->is_ready) + + // set parameters + olap->ready_succeeded = succeeded; + olap->ready_bytes = bytes; + + // insert to IOCP ready list + LinkedList1_Append(&reactor->iocp_ready_list, &olap->ready_list_node); + + // set ready + olap->is_ready = 1; +} + +#endif + +#ifdef BADVPN_USE_EPOLL + +static void set_epoll_fd_pointers (BReactor *bsys) +{ + // Write pointers to our entry pointers into file descriptors. + // If a handler function frees some other file descriptor, the + // free routine will set our pointer to NULL so we don't dispatch it. + for (int i = 0; i < bsys->epoll_results_num; i++) { + struct epoll_event *event = &bsys->epoll_results[i]; + ASSERT(event->data.ptr) + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(!bfd->epoll_returned_ptr) + bfd->epoll_returned_ptr = (BFileDescriptor **)&event->data.ptr; + } +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void set_kevent_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->kevent_results_num; i++) { + struct kevent *event = &bsys->kevent_results[i]; + ASSERT(event->udata) + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(!bfd->kevent_returned_ptr) + bfd->kevent_returned_ptr = (int **)&event->udata; + } break; + + case KEVENT_TAG_KEVENT: { + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(!kev->kevent_returned_ptr) + kev->kevent_returned_ptr = (int **)&event->udata; + } break; + + default: + ASSERT(0); + } + } +} + +static void update_kevent_fd_events (BReactor *bsys, BFileDescriptor *bs, int events) +{ + struct kevent event; + + if (!(bs->waitEvents & BREACTOR_READ) && (events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_READ) && !(events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + + if (!(bs->waitEvents & BREACTOR_WRITE) && (events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_WRITE) && !(events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } +} + +#endif + +#ifdef BADVPN_USE_POLL + +static void set_poll_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->poll_results_num; i++) { + BFileDescriptor *bfd = bsys->poll_results_bfds[i]; + ASSERT(bfd) + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + bfd->poll_returned_index = i; + } +} + +#endif + +static void wait_for_events (BReactor *bsys) +{ + // must have processed all pending events + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + #endif + #ifdef BADVPN_USE_EPOLL + ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num) + #endif + #ifdef BADVPN_USE_KEVENT + ASSERT(bsys->kevent_results_pos == bsys->kevent_results_num) + #endif + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_results_pos == bsys->poll_results_num) + #endif + + // clean up epoll results + #ifdef BADVPN_USE_EPOLL + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + #endif + + // clean up kevent results + #ifdef BADVPN_USE_KEVENT + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + #endif + + // clean up poll results + #ifdef BADVPN_USE_POLL + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + #endif + + // timeout vars + int have_timeout = 0; + btime_t timeout_abs; + btime_t now = 0; // to remove warning + + // compute timeout + BSmallTimer *first_timer = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0).link; + if (first_timer) { + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + + // get current time + now = btime_gettime(); + + // if some timers have already timed out, return them immediately + if (move_expired_timers(bsys, now)) { + BLog(BLOG_DEBUG, "Got already expired timers"); + return; + } + + // timeout is first timer, remember absolute time + have_timeout = 1; + timeout_abs = first_timer->absTime; + } + + // wait until the timeout is reached or the file descriptor / handle in ready + while (1) { + // compute timeout + btime_t timeout_rel = 0; // to remove warning + btime_t timeout_rel_trunc = 0; // to remove warning + if (have_timeout) { + timeout_rel = timeout_abs - now; + timeout_rel_trunc = timeout_rel; + } + + // perform wait + + #ifdef BADVPN_USE_WINAPI + + if (have_timeout) { + if (timeout_rel_trunc > INFINITE - 1) { + timeout_rel_trunc = INFINITE - 1; + } + } + + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(bsys->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, (have_timeout ? timeout_rel_trunc : INFINITE)); + + ASSERT_FORCE(olap || have_timeout) + + if (olap || timeout_rel_trunc == timeout_rel) { + if (olap) { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus returned event"); + + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == bsys) + ASSERT(!olap->is_ready) + + set_iocp_ready(olap, (res == TRUE), bytes); + } else { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + BLog(BLOG_DEBUG, "Calling epoll_wait"); + + int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "epoll_wait interrupted"); + goto try_again; + } + perror("epoll_wait"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "epoll_wait returned %d file descriptors", waitres); + bsys->epoll_results_num = waitres; + set_epoll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "epoll_wait timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + struct timespec ts; + if (have_timeout) { + if (timeout_rel_trunc > 86400000) { + timeout_rel_trunc = 86400000; + } + ts.tv_sec = timeout_rel_trunc / 1000; + ts.tv_nsec = (timeout_rel_trunc % 1000) * 1000000; + } + + BLog(BLOG_DEBUG, "Calling kevent"); + + int waitres = kevent(bsys->kqueue_fd, NULL, 0, bsys->kevent_results, BSYSTEM_MAX_RESULTS, (have_timeout ? &ts : NULL)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "kevent interrupted"); + goto try_again; + } + perror("kevent"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "kevent returned %d events", waitres); + bsys->kevent_results_num = waitres; + set_kevent_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "kevent timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + ASSERT(bsys->poll_num_enabled_fds >= 0) + ASSERT(bsys->poll_num_enabled_fds <= BSYSTEM_MAX_POLL_FDS) + int num_fds = 0; + + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->poll_enabled_fds_list); + while (list_node) { + BFileDescriptor *bfd = UPPER_OBJECT(list_node, BFileDescriptor, poll_enabled_fds_list_node); + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + + // calculate poll events + int pevents = 0; + if ((bfd->waitEvents & BREACTOR_READ)) { + pevents |= POLLIN; + } + if ((bfd->waitEvents & BREACTOR_WRITE)) { + pevents |= POLLOUT; + } + + // write pollfd entry + struct pollfd *pfd = &bsys->poll_results_pollfds[num_fds]; + pfd->fd = bfd->fd; + pfd->events = pevents; + pfd->revents = 0; + + // write BFileDescriptor reference entry + bsys->poll_results_bfds[num_fds] = bfd; + + // increment number of fds in array + num_fds++; + + list_node = LinkedList1Node_Next(list_node); + } + + BLog(BLOG_DEBUG, "Calling poll"); + + int waitres = poll(bsys->poll_results_pollfds, num_fds, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "poll interrupted"); + goto try_again; + } + perror("poll"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "poll returned %d file descriptors", waitres); + bsys->poll_results_num = num_fds; + bsys->poll_results_pos = 0; + set_poll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "poll timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + try_again: + if (have_timeout) { + // get current time + now = btime_gettime(); + // check if we already reached the time we're waiting for + if (now >= timeout_abs) { + BLog(BLOG_DEBUG, "already timed out while trying again"); + move_first_timers(bsys); + break; + } + } + } + + // reset limit objects + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&bsys->active_limits_list)) { + BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node); + ASSERT(limit->count > 0) + limit->count = 0; + LinkedList1_Remove(&bsys->active_limits_list, &limit->active_limits_list_node); + } +} + +#ifndef BADVPN_USE_WINAPI + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user) +{ + bs->fd = fd; + bs->handler = handler; + bs->user = user; + bs->active = 0; +} + +#endif + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + bt->handler.smalll = handler; + bt->state = TIMER_STATE_INACTIVE; + bt->is_small = 1; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + assert_timer(bt); + + return (bt->state != TIMER_STATE_INACTIVE); +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->base.handler.heavy = handler; + bt->base.state = TIMER_STATE_INACTIVE; + bt->base.is_small = 0; + bt->user = user; + bt->msTime = msTime; +} + +int BTimer_IsRunning (BTimer *bt) +{ + return BSmallTimer_IsRunning(&bt->base); +} + +int BReactor_Init (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Reactor initializing"); + + // set not exiting + bsys->exiting = 0; + + // init jobs + BPendingGroup_Init(&bsys->pending_jobs); + + // init timers + BReactor__TimersTree_Init(&bsys->timers_tree); + LinkedList1_Init(&bsys->timers_expired_list); + + // init limits + LinkedList1_Init(&bsys->active_limits_list); + + #ifdef BADVPN_USE_WINAPI + + // init IOCP list + LinkedList1_Init(&bsys->iocp_list); + + // init IOCP handle + if (!(bsys->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1))) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail0; + } + + // init IOCP ready list + LinkedList1_Init(&bsys->iocp_ready_list); + + #endif + + #ifdef BADVPN_USE_EPOLL + + // create epoll fd + if ((bsys->efd = epoll_create(10)) < 0) { + BLog(BLOG_ERROR, "epoll_create failed"); + goto fail0; + } + + // init results array + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // create kqueue fd + if ((bsys->kqueue_fd = kqueue()) < 0) { + BLog(BLOG_ERROR, "kqueue failed"); + goto fail0; + } + + // init results array + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_POLL + + // init enabled fds list + LinkedList1_Init(&bsys->poll_enabled_fds_list); + + // set zero enabled fds + bsys->poll_num_enabled_fds = 0; + + // allocate results arrays + if (!(bsys->poll_results_pollfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_pollfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + if (!(bsys->poll_results_bfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_bfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + // init results array + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + + #endif + + DebugObject_Init(&bsys->d_obj); + #ifndef BADVPN_USE_WINAPI + DebugCounter_Init(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Init(&bsys->d_kevent_ctr); + #endif + DebugCounter_Init(&bsys->d_limits_ctr); + + return 1; + + #ifdef BADVPN_USE_POLL +fail1: + BFree(bsys->poll_results_pollfds); + #endif +fail0: + BPendingGroup_Free(&bsys->pending_jobs); + BLog(BLOG_ERROR, "Reactor failed to initialize"); + return 0; +} + +void BReactor_Free (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + #ifdef BADVPN_USE_WINAPI + while (!LinkedList1_IsEmpty(&bsys->iocp_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetLast(&bsys->iocp_list), BReactorIOCPOverlapped, iocp_list_node); + ASSERT(olap->reactor == bsys) + olap->handler(olap->user, BREACTOR_IOCP_EVENT_EXITING, 0); + } + #endif + + // {pending group has no BPending objects} + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(BReactor__TimersTree_IsEmpty(&bsys->timers_tree)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list)) + DebugObject_Free(&bsys->d_obj); + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_list)) + #endif + #ifndef BADVPN_USE_WINAPI + DebugCounter_Free(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Free(&bsys->d_kevent_ctr); + #endif + DebugCounter_Free(&bsys->d_limits_ctr); + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_num_enabled_fds == 0) + ASSERT(LinkedList1_IsEmpty(&bsys->poll_enabled_fds_list)) + #endif + + BLog(BLOG_DEBUG, "Reactor freeing"); + + #ifdef BADVPN_USE_WINAPI + + // close IOCP handle + ASSERT_FORCE(CloseHandle(bsys->iocp_handle)) + + #endif + + #ifdef BADVPN_USE_EPOLL + + // close epoll fd + ASSERT_FORCE(close(bsys->efd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + // close kqueue fd + ASSERT_FORCE(close(bsys->kqueue_fd) == 0) + + #endif + + #ifdef BADVPN_USE_POLL + + // free results arrays + BFree(bsys->poll_results_bfds); + BFree(bsys->poll_results_pollfds); + + #endif + + // free jobs + BPendingGroup_Free(&bsys->pending_jobs); +} + +int BReactor_Exec (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Entering event loop"); + + while (!bsys->exiting) { + // dispatch job + if (BPendingGroup_HasJobs(&bsys->pending_jobs)) { + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + continue; + } + + // dispatch timer + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->timers_expired_list); + if (list_node) { + BSmallTimer *timer = UPPER_OBJECT(list_node, BSmallTimer, u.list_node); + ASSERT(timer->state == TIMER_STATE_EXPIRED) + + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &timer->u.list_node); + + // set inactive + timer->state = TIMER_STATE_INACTIVE; + + // call handler + BLog(BLOG_DEBUG, "Dispatching timer"); + if (timer->is_small) { + timer->handler.smalll(timer); + } else { + BTimer *btimer = UPPER_OBJECT(timer, BTimer, base); + timer->handler.heavy(btimer->user); + } + continue; + } + + #ifdef BADVPN_USE_WINAPI + + if (!LinkedList1_IsEmpty(&bsys->iocp_ready_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetFirst(&bsys->iocp_ready_list), BReactorIOCPOverlapped, ready_list_node); + ASSERT(olap->is_ready) + ASSERT(olap->handler) + + // remove from ready list + LinkedList1_Remove(&bsys->iocp_ready_list, &olap->ready_list_node); + + // set not ready + olap->is_ready = 0; + + int event = (olap->ready_succeeded ? BREACTOR_IOCP_EVENT_SUCCEEDED : BREACTOR_IOCP_EVENT_FAILED); + + // call handler + olap->handler(olap->user, event, olap->ready_bytes); + continue; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + // dispatch file descriptor + if (bsys->epoll_results_pos < bsys->epoll_results_num) { + // grab event + struct epoll_event *event = &bsys->epoll_results[bsys->epoll_results_pos]; + bsys->epoll_results_pos++; + + // check if the BFileDescriptor was removed + if (!event->data.ptr) { + continue; + } + + // get BFileDescriptor + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(bfd->epoll_returned_ptr == (BFileDescriptor **)&event->data.ptr) + + // zero pointer to the epoll entry + bfd->epoll_returned_ptr = NULL; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && (event->events&EPOLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && (event->events&EPOLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((event->events&EPOLLERR)) { + events |= BREACTOR_ERROR; + } + if ((event->events&EPOLLHUP)) { + events |= BREACTOR_HUP; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // dispatch kevent + if (bsys->kevent_results_pos < bsys->kevent_results_num) { + // grab event + struct kevent *event = &bsys->kevent_results[bsys->kevent_results_pos]; + bsys->kevent_results_pos++; + + // check if the event was removed + if (!event->udata) { + continue; + } + + // check tag + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + // get BFileDescriptor + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(bfd->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + bfd->kevent_returned_ptr = NULL; + + // calculate event to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && event->filter == EVFILT_READ) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && event->filter == EVFILT_WRITE) { + events |= BREACTOR_WRITE; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } break; + + case KEVENT_TAG_KEVENT: { + // get BReactorKEvent + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(kev->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + kev->kevent_returned_ptr = NULL; + + // call handler + BLog(BLOG_DEBUG, "Dispatching kevent"); + kev->handler(kev->user, event->fflags, event->data); + continue; + } break; + + default: + ASSERT(0); + } + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_results_pos < bsys->poll_results_num) { + // grab event + struct pollfd *pfd = &bsys->poll_results_pollfds[bsys->poll_results_pos]; + BFileDescriptor *bfd = bsys->poll_results_bfds[bsys->poll_results_pos]; + bsys->poll_results_pos++; + + // skip removed entry + if (!bfd) { + continue; + } + + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == bsys->poll_results_pos - 1) + + // remove result reference + bfd->poll_returned_index = -1; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents & BREACTOR_READ) && (pfd->revents & POLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents & BREACTOR_WRITE) && (pfd->revents & POLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((pfd->revents & POLLERR) || (pfd->revents & POLLHUP)) { + events |= BREACTOR_ERROR; + } + + if (!events) { + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + wait_for_events(bsys); + } + + BLog(BLOG_DEBUG, "Exiting event loop, exit code %d", bsys->exit_code); + + return bsys->exit_code; +} + +void BReactor_Quit (BReactor *bsys, int code) +{ + bsys->exiting = 1; + bsys->exit_code = code; +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + assert_timer(bt); + ASSERT(mode == BTIMER_SET_ABSOLUTE || mode == BTIMER_SET_RELATIVE) + + // unlink it if it's already in the list + BReactor_RemoveSmallTimer(bsys, bt); + + // if mode is relative, add current time + if (mode == BTIMER_SET_RELATIVE) { + time = btime_add(btime_gettime(), time); + } + + // set time + bt->absTime = time; + + // set running + bt->state = TIMER_STATE_RUNNING; + + // insert to running timers tree + BReactor__TimersTreeRef ref = {bt, bt}; + int res = BReactor__TimersTree_Insert(&bsys->timers_tree, 0, ref, NULL); + ASSERT_EXECUTE(res) +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + assert_timer(bt); + + if (bt->state == TIMER_STATE_INACTIVE) { + return; + } + + if (bt->state == TIMER_STATE_EXPIRED) { + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &bt->u.list_node); + } else { + // remove from running tree + BReactor__TimersTreeRef ref = {bt, bt}; + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + } + + // set inactive + bt->state = TIMER_STATE_INACTIVE; +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after); +} + +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time); +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + return BReactor_RemoveSmallTimer(bsys, &bt->base); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + return &bsys->pending_jobs; +} + +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref) +{ + ASSERT(ref) + + while (!bsys->exiting) { + ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs)) + + if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) { + return 1; + } + + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + } + + return 0; +} + +#ifndef BADVPN_USE_WINAPI + +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(!bs->active) + + #ifdef BADVPN_USE_EPOLL + + // add epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = 0; + event.data.ptr = bs; + if (epoll_ctl(bsys->efd, EPOLL_CTL_ADD, bs->fd, &event) < 0) { + int error = errno; + BLog(BLOG_ERROR, "epoll_ctl failed: %d", error); + return 0; + } + + // set epoll returned pointer + bs->epoll_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // set kevent tag + bs->kevent_tag = KEVENT_TAG_FD; + + // set kevent returned pointer + bs->kevent_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_num_enabled_fds == BSYSTEM_MAX_POLL_FDS) { + BLog(BLOG_ERROR, "too many fds"); + return 0; + } + + // append to enabled fds list + LinkedList1_Append(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds++; + + // set not returned + bs->poll_returned_index = -1; + + #endif + + bs->active = 1; + bs->waitEvents = 0; + + DebugCounter_Increment(&bsys->d_fds_counter); + return 1; +} + +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(bs->active) + DebugCounter_Decrement(&bsys->d_fds_counter); + + bs->active = 0; + + #ifdef BADVPN_USE_EPOLL + + // delete epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_DEL, bs->fd, &event) == 0) + + // write through epoll returned pointer + if (bs->epoll_returned_ptr) { + *bs->epoll_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // delete kevents + update_kevent_fd_events(bsys, bs, 0); + + // write through kevent returned pointer + if (bs->kevent_returned_ptr) { + *bs->kevent_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_POLL + + // invalidate results entry + if (bs->poll_returned_index != -1) { + ASSERT(bs->poll_returned_index >= bsys->poll_results_pos) + ASSERT(bs->poll_returned_index < bsys->poll_results_num) + ASSERT(bsys->poll_results_bfds[bs->poll_returned_index] == bs) + + bsys->poll_results_bfds[bs->poll_returned_index] = NULL; + } + + // remove from enabled fds list + LinkedList1_Remove(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds--; + + #endif +} + +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events) +{ + ASSERT(bs->active) + ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE))) + + if (bs->waitEvents == events) { + return; + } + + #ifdef BADVPN_USE_EPOLL + + // calculate epoll events + int eevents = 0; + if ((events & BREACTOR_READ)) { + eevents |= EPOLLIN; + } + if ((events & BREACTOR_WRITE)) { + eevents |= EPOLLOUT; + } + + // update epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = eevents; + event.data.ptr = bs; + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_MOD, bs->fd, &event) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + update_kevent_fd_events(bsys, bs, events); + + #endif + + // update events + bs->waitEvents = events; +} + +#endif + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit) +{ + DebugObject_Access(&reactor->d_obj); + ASSERT(limit > 0) + + // init arguments + o->reactor = reactor; + o->limit = limit; + + // set count zero + o->count = 0; + + DebugCounter_Increment(&reactor->d_limits_ctr); + DebugObject_Init(&o->d_obj); +} + +void BReactorLimit_Free (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&reactor->d_limits_ctr); + + // remove from active limits list + if (o->count > 0) { + LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node); + } +} + +int BReactorLimit_Increment (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // check count against limit + if (o->count >= o->limit) { + return 0; + } + + // increment count + o->count++; + + // if limit was zero, add to active limits list + if (o->count == 1) { + LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node); + } + + return 1; +} + +void BReactorLimit_SetLimit (BReactorLimit *o, int limit) +{ + DebugObject_Access(&o->d_obj); + ASSERT(limit > 0) + + // set limit + o->limit = limit; +} + +#ifdef BADVPN_USE_KEVENT + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + o->ident = ident; + o->filter = filter; + + // add kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_ADD; + event.fflags = fflags; + event.data = data; + event.udata = &o->kevent_tag; + if (kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) < 0) { + return 0; + } + + // set kevent tag + o->kevent_tag = KEVENT_TAG_KEVENT; + + // set kevent returned pointer + o->kevent_returned_ptr = NULL; + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&o->reactor->d_kevent_ctr); + return 1; +} + +void BReactorKEvent_Free (BReactorKEvent *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->reactor->d_kevent_ctr); + + // write through kevent returned pointer + if (o->kevent_returned_ptr) { + *o->kevent_returned_ptr = NULL; + } + + // delete kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) +} + +#endif + +#ifdef BADVPN_USE_WINAPI + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor) +{ + DebugObject_Access(&reactor->d_obj); + + return reactor->iocp_handle; +} + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // zero overlapped + memset(&o->olap, 0, sizeof(o->olap)); + + // append to IOCP list + LinkedList1_Append(&reactor->iocp_list, &o->iocp_list_node); + + // set not ready + o->is_ready = 0; + + DebugObject_Init(&o->d_obj); +} + +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + + // remove from IOCP ready list + if (o->is_ready) { + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + } + + // remove from IOCP list + LinkedList1_Remove(&reactor->iocp_list, &o->iocp_list_node); +} + +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // wait for IOCP events until we get an event for this olap + while (!o->is_ready) { + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(reactor->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, INFINITE); + + ASSERT_FORCE(olap) + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == reactor) + + // regular I/O should be done synchronously, so we shoudln't ever get a second completion before an + // existing one is dispatched. If however PostQueuedCompletionStatus is being used to signal events, + // just discard any excess events. + if (!olap->is_ready) { + set_iocp_ready(olap, (res == TRUE), bytes); + } + } + + // remove from IOCP ready list + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + + // set not ready + o->is_ready = 0; + + if (out_succeeded) { + *out_succeeded = o->ready_succeeded; + } + if (out_bytes) { + *out_bytes = o->ready_bytes; + } +} + +#endif diff --git a/app/src/main/jni/badvpn/system/BReactor_badvpn.h b/app/src/main/jni/badvpn/system/BReactor_badvpn.h new file mode 100644 index 0000000..2c5ab4c --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_badvpn.h @@ -0,0 +1,572 @@ +/** + * @file BReactor_badvpn.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_USE_EPOLL) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_POLL)) != 1 +#error Unknown event backend or too many event backends +#endif + +#ifdef BADVPN_USE_WINAPI +#include +#endif + +#ifdef BADVPN_USE_EPOLL +#include +#endif + +#ifdef BADVPN_USE_KEVENT +#include +#include +#include +#endif + +#ifdef BADVPN_USE_POLL +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +struct BSmallTimer_t; +typedef struct BSmallTimer_t *BReactor_timerstree_link; + +#include "BReactor_badvpn_timerstree.h" +#include + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param timer pointer to the timer. Use the {@link UPPER_OBJECT} macro + * to obtain the pointer to the containing structure. + */ +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param user value passed to {@link BTimer_Init} + */ +typedef void (*BTimer_handler) (void *user); + +/** + * Timer object used with {@link BReactor}. + */ +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + union { + LinkedList1Node list_node; + struct BSmallTimer_t *tree_child[2]; + } u; + struct BSmallTimer_t *tree_parent; + btime_t absTime; + int8_t tree_balance; + uint8_t state; + uint8_t is_small; +} BSmallTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param handler handler function invoked when the timer expires + */ +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BSmallTimer_IsRunning (BSmallTimer *bt); + +/** + * Timer object used with {@link BReactor}. This is a legacy wrapper + * around {@link BSmallTimer} with an extra field for the default time. + */ +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param msTime default timeout in milliseconds + * @param handler handler function invoked when the timer expires + * @param user value to pass to the handler function + */ +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BTimer_IsRunning (BTimer *bt); + +#ifndef BADVPN_USE_WINAPI + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +/** + * Handler function invoked by the reactor when one or more events are detected. + * The events argument will contain a subset of the monitored events (BREACTOR_READ, BREACTOR_WRITE), + * plus possibly the error event (BREACTOR_ERROR). + * The file descriptor object is in active state, being called from within + * the associated reactor. + * + * @param user value passed to {@link BFileDescriptor_Init} + * @param events bitmask composed of a subset of monitored events (BREACTOR_READ, BREACTOR_WRITE), + * and possibly the error event BREACTOR_ERROR and the hang-up event BREACTOR_HUP. + * Will be nonzero. + */ +typedef void (*BFileDescriptor_handler) (void *user, int events); + +/** + * File descriptor object used with {@link BReactor}. + */ +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + + #ifdef BADVPN_USE_EPOLL + struct BFileDescriptor_t **epoll_returned_ptr; + #endif + + #ifdef BADVPN_USE_KEVENT + int kevent_tag; + int **kevent_returned_ptr; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1Node poll_enabled_fds_list_node; + int poll_returned_index; + #endif +} BFileDescriptor; + +/** + * Intializes the file descriptor object. + * The object is initialized in not active state. + * + * @param bs file descriptor object to initialize + * @param fb file descriptor to represent + * @param handler handler function invoked by the reactor when a monitored event is detected + * @param user value passed to the handler functuon + */ +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +#endif + +// BReactor + +#define BSYSTEM_MAX_RESULTS 64 +#define BSYSTEM_MAX_HANDLES 64 +#define BSYSTEM_MAX_POLL_FDS 4096 + +/** + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ +typedef struct { + int exiting; + int exit_code; + + // jobs + BPendingGroup pending_jobs; + + // timers + BReactor__TimersTree timers_tree; + LinkedList1 timers_expired_list; + + // limits + LinkedList1 active_limits_list; + + #ifdef BADVPN_USE_WINAPI + LinkedList1 iocp_list; + HANDLE iocp_handle; + LinkedList1 iocp_ready_list; + #endif + + #ifdef BADVPN_USE_EPOLL + int efd; // epoll fd + struct epoll_event epoll_results[BSYSTEM_MAX_RESULTS]; // epoll returned events buffer + int epoll_results_num; // number of events in the array + int epoll_results_pos; // number of events processed so far + #endif + + #ifdef BADVPN_USE_KEVENT + int kqueue_fd; + struct kevent kevent_results[BSYSTEM_MAX_RESULTS]; + int kevent_results_num; + int kevent_results_pos; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1 poll_enabled_fds_list; + int poll_num_enabled_fds; + int poll_results_num; + int poll_results_pos; + struct pollfd *poll_results_pollfds; + BFileDescriptor **poll_results_bfds; + #endif + + DebugObject d_obj; + #ifndef BADVPN_USE_WINAPI + DebugCounter d_fds_counter; + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter d_kevent_ctr; + #endif + DebugCounter d_limits_ctr; +} BReactor; + +/** + * Initializes the reactor. + * {@link BLog_Init} must have been done. + * {@link BTime_Init} must have been done. + * + * @param bsys the object + * @return 1 on success, 0 on failure + */ +int BReactor_Init (BReactor *bsys) WARN_UNUSED; + +/** + * Frees the reactor. + * Must not be called from within the event loop ({@link BReactor_Exec}). + * There must be no {@link BPending} or {@link BSmallPending} objects using the + * pending group returned by {@link BReactor_PendingGroup}. + * There must be no running timers in this reactor. + * There must be no limit objects in this reactor. + * There must be no file descriptors or handles registered + * with this reactor. + * There must be no {@link BReactorKEvent} objects in this reactor. + * + * @param bsys the object + */ +void BReactor_Free (BReactor *bsys); + +/** + * Runs the event loop. + * + * @param bsys the object + * @return value passed to {@link BReactor_Quit} + */ +int BReactor_Exec (BReactor *bsys); + +/** + * Causes the event loop ({@link BReactor_Exec}) to cease + * dispatching events and return. + * Any further calls of {@link BReactor_Exec} will return immediately. + * + * @param bsys the object + * @param code value {@link BReactor_Exec} should return. If this is + * called more than once, it will return the last code. + */ +void BReactor_Quit (BReactor *bsys, int code); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BSmallTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param mode interpretation of time (BTIMER_SET_ABSOLUTE or BTIMER_SET_RELATIVE) + * @param time absolute or relative expiration time + */ +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +/** + * Starts a timer to expire after its default time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + */ +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); + +/** + * Starts a timer to expire after a given time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param after relative expiration time + */ +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * The timer's expiration time is set to the time argument. + * + * @param bsys the object + * @param bt timer to start + * @param time absolute expiration time (according to {@link btime_gettime}) + */ +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +/** + * Returns a {@link BPendingGroup} object that can be used to schedule jobs for + * the reactor to execute. These jobs have complete priority over other events + * (timers, file descriptors and Windows handles). + * The returned pending group may only be used as an argument to {@link BPending_Init}, + * and must not be accessed by other means. + * All {@link BPending} and {@link BSmallPending} objects using this group must be + * freed before freeing the reactor. + * + * @param bsys the object + * @return pending group for scheduling jobs for the reactor to execute + */ +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +/** + * Executes pending jobs until either: + * - the reference job is reached, or + * - {@link BReactor_Quit} is called. + * The reference job must be reached before the job list empties. + * The reference job will not be executed. + * + * WARNING: Use with care. This should only be used to to work around third-party software + * that does not integrade into the jobs system. In particular, you should think about: + * - the effects the jobs to be executed may have, and + * - the environment those jobs expect to be executed in. + * + * @param bsys the object + * @param ref reference job. It is not accessed in any way, only its address is compared to + * pending jobs before they are executed. + * @return 1 if the reference job was reached, + * 0 if {@link BReactor_Quit} was called (either while executing a job, or before) + */ +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); + +#ifndef BADVPN_USE_WINAPI + +/** + * Starts monitoring a file descriptor. + * + * @param bsys the object + * @param bs file descriptor object. Must have been initialized with + * {@link BFileDescriptor_Init} Must be in not active state. + * On success, the file descriptor object enters active state, + * associated with this reactor. + * @return 1 on success, 0 on failure + */ +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; + +/** + * Stops monitoring a file descriptor. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. The file descriptor object + * enters not active state. + */ +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); + +/** + * Sets monitored file descriptor events. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. + * @param events events to watch for. Must not have any bits other than + * BREACTOR_READ and BREACTOR_WRITE. + * This overrides previosly monitored events. + */ +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +#endif + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +/** + * Initializes a limit object. + * A limit object consists of a counter integer, which is initialized to + * zero, is incremented by {@link BReactorLimit_Increment} up to \a limit, + * and is reset to zero every time the event loop performs a wait. + * If the event loop has processed all detected events, and before performing + * a wait, it determines that timers have expired, the counter will not be reset. + * + * @param o the object + * @param reactor reactor the object is tied to + * @param limit maximum counter value. Must be >0. + */ +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); + +/** + * Frees a limit object. + * + * @param o the object + */ +void BReactorLimit_Free (BReactorLimit *o); + +/** + * Attempts to increment the counter of a limit object. + * + * @param o the object + * @return 1 if the counter was lesser than the limit and was incremented, + * 0 if the counter was greater or equal to the limit and could not be + * incremented + */ +int BReactorLimit_Increment (BReactorLimit *o); + +/** + * Sets the limit of a limit object. + * + * @param o the object + * @param limit new limit. Must be >0. + */ +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#ifdef BADVPN_USE_KEVENT + +typedef void (*BReactorKEvent_handler) (void *user, u_int fflags, intptr_t data); + +typedef struct { + BReactor *reactor; + BReactorKEvent_handler handler; + void *user; + uintptr_t ident; + short filter; + int kevent_tag; + int **kevent_returned_ptr; + DebugObject d_obj; +} BReactorKEvent; + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data); +void BReactorKEvent_Free (BReactorKEvent *o); + +#endif + +#ifdef BADVPN_USE_WINAPI + +#define BREACTOR_IOCP_EVENT_SUCCEEDED 1 +#define BREACTOR_IOCP_EVENT_FAILED 2 +#define BREACTOR_IOCP_EVENT_EXITING 3 + +typedef void (*BReactorIOCPOverlapped_handler) (void *user, int event, DWORD bytes); + +typedef struct { + OVERLAPPED olap; + BReactor *reactor; + void *user; + BReactorIOCPOverlapped_handler handler; + LinkedList1Node iocp_list_node; + int is_ready; + LinkedList1Node ready_list_node; + int ready_succeeded; + DWORD ready_bytes; + DebugObject d_obj; +} BReactorIOCPOverlapped; + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor); + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler); +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o); +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes); + +#endif + +#endif diff --git a/app/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h b/app/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h new file mode 100644 index 0000000..3cecd75 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h @@ -0,0 +1,13 @@ +#define CAVL_PARAM_NAME BReactor__TimersTree +#define CAVL_PARAM_FEATURE_COUNTS 0 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS 1 +#define CAVL_PARAM_TYPE_ENTRY struct BSmallTimer_t +#define CAVL_PARAM_TYPE_LINK BReactor_timerstree_link +#define CAVL_PARAM_TYPE_ARG int +#define CAVL_PARAM_VALUE_NULL ((BReactor_timerstree_link)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_timers((entry1).link, (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD u.tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent diff --git a/app/src/main/jni/badvpn/system/BReactor_emscripten.c b/app/src/main/jni/badvpn/system/BReactor_emscripten.c new file mode 100644 index 0000000..f90af55 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_emscripten.c @@ -0,0 +1,176 @@ +/** + * @file BReactor_emscripten.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "BReactor_emscripten.h" + +#include + +#include + +static void assert_timer (BTimer *bt, BReactor *reactor) +{ + ASSERT(bt->active == 0 || bt->active == 1); + ASSERT(bt->active == 0 || bt->reactor); + ASSERT(bt->active == 0 || (!reactor || bt->reactor == reactor)); +} + +static void dispatch_pending (BReactor *o) +{ + while (BPendingGroup_HasJobs(&o->pending_jobs)) { + BPendingGroup_ExecuteJob(&o->pending_jobs); + } +} + +__attribute__((used)) +void breactor_timer_cb (BReactor *reactor, BTimer *bt) +{ + assert_timer(bt, reactor); + ASSERT(bt->active); + ASSERT(!BPendingGroup_HasJobs(&reactor->pending_jobs)); + + bt->active = 0; + + bt->handler(bt->handler_pointer); + dispatch_pending(reactor); +} + +static void small_timer_handler (void *vbt) +{ + BSmallTimer *bt = vbt; + + bt->handler(bt); +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->msTime = msTime; + bt->handler = handler; + bt->handler_pointer = user; + bt->active = 0; +} + +int BTimer_IsRunning (BTimer *bt) +{ + assert_timer(bt, NULL); + + return bt->active; +} + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + BTimer_Init(&bt->timer, 0, small_timer_handler, bt); + bt->handler = handler; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + return BTimer_IsRunning(&bt->timer); +} + +void BReactor_EmscriptenInit (BReactor *bsys) +{ + BPendingGroup_Init(&bsys->pending_jobs); + + DebugObject_Init(&bsys->d_obj); +} + +void BReactor_EmscriptenFree (BReactor *bsys) +{ + DebugObject_Free(&bsys->d_obj); + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)); +} + +void BReactor_EmscriptenSync (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + dispatch_pending(bsys); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return &bsys->pending_jobs; +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetTimerAfter(bsys, bt, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt, bsys); + + if (bt->active) { + BReactor_RemoveTimer(bsys, bt); + } + + char cmd[120]; + sprintf(cmd, "setTimeout(function(){Module.ccall('breactor_timer_cb','null',['number','number'],[%d,%d]);},%"PRIi64")", (int)bsys, (int)bt, after); + + bt->active = 1; + bt->timerid = emscripten_run_script_int(cmd); + bt->reactor = bsys; +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt, bsys); + + if (!bt->active) { + return; + } + + char cmd[30]; + sprintf(cmd, "clearTimeout(%d)", bt->timerid); + + emscripten_run_script(cmd); + bt->active = 0; +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + ASSERT(mode == BTIMER_SET_RELATIVE) + + BReactor_SetTimerAfter(bsys, &bt->timer, time); +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + BReactor_RemoveTimer(bsys, &bt->timer); +} diff --git a/app/src/main/jni/badvpn/system/BReactor_emscripten.h b/app/src/main/jni/badvpn/system/BReactor_emscripten.h new file mode 100644 index 0000000..c004d16 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_emscripten.h @@ -0,0 +1,87 @@ +/** + * @file BReactor_emscripten.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include +#include +#include + +#define BTIMER_SET_RELATIVE 2 + +typedef struct BReactor_s BReactor; + +typedef void (*BTimer_handler) (void *user); + +typedef struct BTimer_t { + btime_t msTime; + BTimer_handler handler; + void *handler_pointer; + uint8_t active; + int timerid; + BReactor *reactor; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BSmallTimer_t; + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +typedef struct BSmallTimer_t { + BTimer timer; + BSmallTimer_handler handler; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +struct BReactor_s { + BPendingGroup pending_jobs; + DebugObject d_obj; +}; + +void BReactor_EmscriptenInit (BReactor *bsys); +void BReactor_EmscriptenFree (BReactor *bsys); +void BReactor_EmscriptenSync (BReactor *bsys); + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +#endif diff --git a/app/src/main/jni/badvpn/system/BReactor_glib.c b/app/src/main/jni/badvpn/system/BReactor_glib.c new file mode 100644 index 0000000..63f0fd6 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_glib.c @@ -0,0 +1,524 @@ +/** + * @file BReactor_glib.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include "BReactor_glib.h" + +#include + +struct fd_source { + GSource source; + BFileDescriptor *bfd; +}; + +static void assert_timer (BSmallTimer *bt) +{ + ASSERT(bt->is_small == 0 || bt->is_small == 1) + ASSERT(bt->active == 0 || bt->active == 1) + ASSERT(!bt->active || bt->reactor) + ASSERT(!bt->active || bt->source) +} + +static void dispatch_pending (BReactor *o) +{ + while (!o->exiting && BPendingGroup_HasJobs(&o->pending_jobs)) { + BPendingGroup_ExecuteJob(&o->pending_jobs); + } +} + +static void reset_limits (BReactor *o) +{ + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->active_limits_list)) { + BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node); + ASSERT(limit->count > 0) + limit->count = 0; + LinkedList1_Remove(&o->active_limits_list, &limit->active_limits_list_node); + } +} + +static gushort get_glib_wait_events (int ev) +{ + gushort gev = G_IO_ERR | G_IO_HUP; + + if (ev & BREACTOR_READ) { + gev |= G_IO_IN; + } + + if (ev & BREACTOR_WRITE) { + gev |= G_IO_OUT; + } + + return gev; +} + +static int get_fd_dispatchable_events (BFileDescriptor *bfd) +{ + ASSERT(bfd->active) + + int ev = 0; + + if ((bfd->waitEvents & BREACTOR_READ) && (bfd->pollfd.revents & G_IO_IN)) { + ev |= BREACTOR_READ; + } + + if ((bfd->waitEvents & BREACTOR_WRITE) && (bfd->pollfd.revents & G_IO_OUT)) { + ev |= BREACTOR_WRITE; + } + + if ((bfd->pollfd.revents & G_IO_ERR)) { + ev |= BREACTOR_ERROR; + } + + if ((bfd->pollfd.revents & G_IO_HUP)) { + ev |= BREACTOR_HUP; + } + + return ev; +} + +static gboolean timer_source_handler (gpointer data) +{ + BSmallTimer *bt = (void *)data; + assert_timer(bt); + ASSERT(bt->active) + + BReactor *reactor = bt->reactor; + + if (reactor->exiting) { + return FALSE; + } + + g_source_destroy(bt->source); + g_source_unref(bt->source); + bt->active = 0; + DebugCounter_Decrement(&reactor->d_timers_ctr); + + if (bt->is_small) { + bt->handler.smalll(bt); + } else { + BTimer *btimer = UPPER_OBJECT(bt, BTimer, base); + bt->handler.heavy(btimer->user); + } + + dispatch_pending(reactor); + reset_limits(reactor); + + return FALSE; +} + +static gboolean fd_source_func_prepare (GSource *source, gint *timeout) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + *timeout = -1; + return FALSE; +} + +static gboolean fd_source_func_check (GSource *source) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + return (get_fd_dispatchable_events(bfd) ? TRUE : FALSE); +} + +static gboolean fd_source_func_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + BReactor *reactor = bfd->reactor; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + if (reactor->exiting) { + return TRUE; + } + + int events = get_fd_dispatchable_events(bfd); + if (!events) { + return TRUE; + } + + bfd->handler(bfd->user, events); + dispatch_pending(reactor); + reset_limits(reactor); + + return TRUE; +} + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + bt->handler.smalll = handler; + bt->active = 0; + bt->is_small = 1; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + assert_timer(bt); + + return bt->active; +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->base.handler.heavy = handler; + bt->base.active = 0; + bt->base.is_small = 0; + bt->user = user; + bt->msTime = msTime; +} + +int BTimer_IsRunning (BTimer *bt) +{ + return BSmallTimer_IsRunning(&bt->base); +} + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user) +{ + bs->fd = fd; + bs->handler = handler; + bs->user = user; + bs->active = 0; +} + +int BReactor_Init (BReactor *bsys) +{ + return BReactor_InitFromExistingGMainLoop(bsys, g_main_loop_new(NULL, FALSE), 1); +} + +void BReactor_Free (BReactor *bsys) +{ + DebugObject_Free(&bsys->d_obj); + DebugCounter_Free(&bsys->d_timers_ctr); + DebugCounter_Free(&bsys->d_limits_ctr); + DebugCounter_Free(&bsys->d_fds_counter); + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list)) + + // free job queue + BPendingGroup_Free(&bsys->pending_jobs); + + // unref main loop if needed + if (bsys->unref_gloop_on_free) { + g_main_loop_unref(bsys->gloop); + } +} + +int BReactor_Exec (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + // dispatch pending jobs (until exiting) and reset limits + dispatch_pending(bsys); + reset_limits(bsys); + + // if exiting, do not enter glib loop + if (bsys->exiting) { + return bsys->exit_code; + } + + // enter glib loop + g_main_loop_run(bsys->gloop); + + ASSERT(bsys->exiting) + + return bsys->exit_code; +} + +void BReactor_Quit (BReactor *bsys, int code) +{ + DebugObject_Access(&bsys->d_obj); + + // remember exiting + bsys->exiting = 1; + bsys->exit_code = code; + + // request termination of glib loop + g_main_loop_quit(bsys->gloop); +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt); + + // remove timer if it's already set + BReactor_RemoveSmallTimer(bsys, bt); + + // if mode is absolute, subtract current time + if (mode == BTIMER_SET_ABSOLUTE) { + btime_t now = btime_gettime(); + time = (time < now ? 0 : time - now); + } + + // set active and reactor + bt->active = 1; + bt->reactor = bsys; + + // init source + bt->source = g_timeout_source_new(time); + g_source_set_callback(bt->source, timer_source_handler, bt, NULL); + g_source_attach(bt->source, g_main_loop_get_context(bsys->gloop)); + + DebugCounter_Increment(&bsys->d_timers_ctr); +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt); + + // do nothing if timer is not active + if (!bt->active) { + return; + } + + // free source + g_source_destroy(bt->source); + g_source_unref(bt->source); + + // set not active + bt->active = 0; + + DebugCounter_Decrement(&bsys->d_timers_ctr); +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after); +} + +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time); +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + return BReactor_RemoveSmallTimer(bsys, &bt->base); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return &bsys->pending_jobs; +} + +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(ref) + + while (!bsys->exiting) { + ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs)) + + if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) { + return 1; + } + + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + } + + return 0; +} + +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(!bs->active) + + // set active, no wait events, and set reactor + bs->active = 1; + bs->waitEvents = 0; + bs->reactor = bsys; + + // create source + bs->source = g_source_new(&bsys->fd_source_funcs, sizeof(struct fd_source)); + ((struct fd_source *)bs->source)->bfd = bs; + + // init pollfd + bs->pollfd.fd = bs->fd; + bs->pollfd.events = get_glib_wait_events(bs->waitEvents); + bs->pollfd.revents = 0; + + // start source + g_source_add_poll(bs->source, &bs->pollfd); + g_source_attach(bs->source, g_main_loop_get_context(bsys->gloop)); + + DebugCounter_Increment(&bsys->d_fds_counter); + return 1; +} + +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + DebugObject_Access(&bsys->d_obj); + DebugCounter_Decrement(&bsys->d_fds_counter); + ASSERT(bs->active) + + // free source + g_source_destroy(bs->source); + g_source_unref(bs->source); + + // set not active + bs->active = 0; +} + +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(bs->active) + ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE))) + + // set new wait events + bs->waitEvents = events; + + // update pollfd wait events + bs->pollfd.events = get_glib_wait_events(bs->waitEvents); +} + +int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free) +{ + ASSERT(gloop) + ASSERT(unref_gloop_on_free == !!unref_gloop_on_free) + + // set not exiting + bsys->exiting = 0; + + // set gloop and unref on free flag + bsys->gloop = gloop; + bsys->unref_gloop_on_free = unref_gloop_on_free; + + // init fd source functions table + memset(&bsys->fd_source_funcs, 0, sizeof(bsys->fd_source_funcs)); + bsys->fd_source_funcs.prepare = fd_source_func_prepare; + bsys->fd_source_funcs.check = fd_source_func_check; + bsys->fd_source_funcs.dispatch = fd_source_func_dispatch; + bsys->fd_source_funcs.finalize = NULL; + + // init job queue + BPendingGroup_Init(&bsys->pending_jobs); + + // init active limits list + LinkedList1_Init(&bsys->active_limits_list); + + DebugCounter_Init(&bsys->d_fds_counter); + DebugCounter_Init(&bsys->d_limits_ctr); + DebugCounter_Init(&bsys->d_timers_ctr); + DebugObject_Init(&bsys->d_obj); + return 1; +} + +GMainLoop * BReactor_GetGMainLoop (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return bsys->gloop; +} + +int BReactor_SynchronizeAll (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + dispatch_pending(bsys); + + return !bsys->exiting; +} + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit) +{ + DebugObject_Access(&reactor->d_obj); + ASSERT(limit > 0) + + // init arguments + o->reactor = reactor; + o->limit = limit; + + // set count zero + o->count = 0; + + DebugCounter_Increment(&reactor->d_limits_ctr); + DebugObject_Init(&o->d_obj); +} + +void BReactorLimit_Free (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&reactor->d_limits_ctr); + + // remove from active limits list + if (o->count > 0) { + LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node); + } +} + +int BReactorLimit_Increment (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // check count against limit + if (o->count >= o->limit) { + return 0; + } + + // increment count + o->count++; + + // if limit was zero, add to active limits list + if (o->count == 1) { + LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node); + } + + return 1; +} + +void BReactorLimit_SetLimit (BReactorLimit *o, int limit) +{ + DebugObject_Access(&o->d_obj); + ASSERT(limit > 0) + + // set limit + o->limit = limit; +} diff --git a/app/src/main/jni/badvpn/system/BReactor_glib.h b/app/src/main/jni/badvpn/system/BReactor_glib.h new file mode 100644 index 0000000..0102be9 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BReactor_glib.h @@ -0,0 +1,148 @@ +/** + * @file BReactor_glib.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct BReactor_s BReactor; + +struct BSmallTimer_t; + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); +typedef void (*BTimer_handler) (void *user); + +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + BReactor *reactor; + GSource *source; + uint8_t active; + uint8_t is_small; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +typedef void (*BFileDescriptor_handler) (void *user, int events); + +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + BReactor *reactor; + GSource *source; + GPollFD pollfd; +} BFileDescriptor; + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +struct BReactor_s { + int exiting; + int exit_code; + GMainLoop *gloop; + int unref_gloop_on_free; + GSourceFuncs fd_source_funcs; + BPendingGroup pending_jobs; + LinkedList1 active_limits_list; + + DebugCounter d_fds_counter; + DebugCounter d_limits_ctr; + DebugCounter d_timers_ctr; + DebugObject d_obj; +}; + +int BReactor_Init (BReactor *bsys) WARN_UNUSED; +void BReactor_Free (BReactor *bsys); +int BReactor_Exec (BReactor *bsys); +void BReactor_Quit (BReactor *bsys, int code); +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free); +GMainLoop * BReactor_GetGMainLoop (BReactor *bsys); +int BReactor_SynchronizeAll (BReactor *bsys); + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); +void BReactorLimit_Free (BReactorLimit *o); +int BReactorLimit_Increment (BReactorLimit *o); +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#endif diff --git a/app/src/main/jni/badvpn/system/BSignal.c b/app/src/main/jni/badvpn/system/BSignal.c new file mode 100644 index 0000000..520a167 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BSignal.c @@ -0,0 +1,188 @@ +/** + * @file BSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +static struct { + int initialized; + int finished; + BReactor *reactor; + BSignal_handler handler; + void *user; + #ifdef BADVPN_USE_WINAPI + BReactorIOCPOverlapped olap; + CRITICAL_SECTION iocp_handle_mutex; + HANDLE iocp_handle; + #else + BUnixSignal signal; + #endif +} bsignal_global = { + 0 +}; + +#ifdef BADVPN_USE_WINAPI + +static void olap_handler (void *user, int event, DWORD bytes) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!(event == BREACTOR_IOCP_EVENT_EXITING) || bsignal_global.finished) + + if (event == BREACTOR_IOCP_EVENT_EXITING) { + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + return; + } + + if (!bsignal_global.finished) { + // call handler + bsignal_global.handler(bsignal_global.user); + return; + } +} + +static BOOL WINAPI ctrl_handler (DWORD type) +{ + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + + if (bsignal_global.iocp_handle) { + PostQueuedCompletionStatus(bsignal_global.iocp_handle, 0, 0, &bsignal_global.olap.olap); + } + + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + return TRUE; +} + +#else + +static void unix_signal_handler (void *user, int signo) +{ + ASSERT(signo == SIGTERM || signo == SIGINT) + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + BLog(BLOG_DEBUG, "Dispatching signal"); + + // call handler + bsignal_global.handler(bsignal_global.user); + return; +} + +#endif + +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) +{ + ASSERT(!bsignal_global.initialized) + + // init arguments + bsignal_global.reactor = reactor; + bsignal_global.handler = handler; + bsignal_global.user = user; + + BLog(BLOG_DEBUG, "BSignal initializing"); + + #ifdef BADVPN_USE_WINAPI + + // init olap + BReactorIOCPOverlapped_Init(&bsignal_global.olap, bsignal_global.reactor, NULL, olap_handler); + + // init handler mutex + InitializeCriticalSection(&bsignal_global.iocp_handle_mutex); + + // remember IOCP handle + bsignal_global.iocp_handle = BReactor_GetIOCPHandle(bsignal_global.reactor); + + // configure ctrl handler + if (!SetConsoleCtrlHandler(ctrl_handler, TRUE)) { + BLog(BLOG_ERROR, "SetConsoleCtrlHandler failed"); + goto fail1; + } + + #else + + sigset_t sset; + ASSERT_FORCE(sigemptyset(&sset) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGTERM) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGINT) == 0) + + // init BUnixSignal + if (!BUnixSignal_Init(&bsignal_global.signal, bsignal_global.reactor, sset, unix_signal_handler, NULL)) { + BLog(BLOG_ERROR, "BUnixSignal_Init failed"); + goto fail0; + } + + #endif + + bsignal_global.initialized = 1; + bsignal_global.finished = 0; + + return 1; + + #ifdef BADVPN_USE_WINAPI +fail1: + DeleteCriticalSection(&bsignal_global.iocp_handle_mutex); + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + #endif + +fail0: + return 0; +} + +void BSignal_Finish (void) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + #ifdef BADVPN_USE_WINAPI + + // forget IOCP handle + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + bsignal_global.iocp_handle = NULL; + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + #else + + // free BUnixSignal + BUnixSignal_Free(&bsignal_global.signal, 0); + + #endif + + bsignal_global.finished = 1; +} diff --git a/app/src/main/jni/badvpn/system/BSignal.h b/app/src/main/jni/badvpn/system/BSignal.h new file mode 100644 index 0000000..41f17e2 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BSignal.h @@ -0,0 +1,64 @@ +/** + * @file BSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for catching program termination requests. + */ + +#ifndef BADVPN_SYSTEM_BSIGNAL_H +#define BADVPN_SYSTEM_BSIGNAL_H + +#include +#include + +typedef void (*BSignal_handler) (void *user); + +/** + * Initializes signal handling. + * The object is created in not capturing state. + * {@link BLog_Init} must have been done. + * + * WARNING: make sure this won't interfere with other components: + * - on Linux, this uses {@link BUnixSignal} to catch SIGTERM and SIGINT, + * - on Windows, this sets up a handler with SetConsoleCtrlHandler. + * + * @param reactor {@link BReactor} from which the handler will be called + * @param handler callback function invoked from the reactor + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Finishes signal handling. + * {@link BSignal_Init} must not be called again. + */ +void BSignal_Finish (void); + +#endif diff --git a/app/src/main/jni/badvpn/system/BThreadSignal.c b/app/src/main/jni/badvpn/system/BThreadSignal.c new file mode 100644 index 0000000..7d68c1b --- /dev/null +++ b/app/src/main/jni/badvpn/system/BThreadSignal.c @@ -0,0 +1,136 @@ +/** + * @file BThreadSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "BThreadSignal.h" + +#include + +static void bfd_handler (void *user, int events) +{ + BThreadSignal *o = user; + DebugObject_Access(&o->d_obj); + + char byte; + ssize_t res = read(o->pipe[0], &byte, sizeof(byte)); + + if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + return; + } + + if (res < 0) { + BLog(BLOG_ERROR, "read failed"); + return; + } + + if (res != sizeof(byte)) { + BLog(BLOG_ERROR, "bad read"); + return; + } + + o->handler(o); +} + +int BThreadSignal_Init (BThreadSignal *o, BReactor *reactor, BThreadSignal_handler handler) +{ + o->reactor = reactor; + o->handler = handler; + + if (pipe(o->pipe) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail0; + } + + if (!badvpn_set_nonblocking(o->pipe[0]) || !badvpn_set_nonblocking(o->pipe[1])) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + BFileDescriptor_Init(&o->bfd, o->pipe[0], bfd_handler, o); + + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->pipe[1]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + if (close(o->pipe[0]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void BThreadSignal_Free (BThreadSignal *o) +{ + DebugObject_Free(&o->d_obj); + + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + if (close(o->pipe[1]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + if (close(o->pipe[0]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +int BThreadSignal_Thread_Signal (BThreadSignal *o) +{ + DebugObject_Access(&o->d_obj); + + char byte = 0; + ssize_t res = write(o->pipe[1], &byte, sizeof(byte)); + + if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + return 0; + } + + if (res < 0) { + BLog(BLOG_ERROR, "write failed"); + } else if (res != sizeof(byte)) { + BLog(BLOG_ERROR, "bad write"); + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/system/BThreadSignal.h b/app/src/main/jni/badvpn/system/BThreadSignal.h new file mode 100644 index 0000000..2c8d816 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BThreadSignal.h @@ -0,0 +1,53 @@ +/** + * @file BThreadSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_THREAD_SIGNAL_H +#define BADVPN_B_THREAD_SIGNAL_H + +#include +#include +#include + +typedef struct BThreadSignal_s BThreadSignal; + +typedef void (*BThreadSignal_handler) (BThreadSignal *thread_signal); + +struct BThreadSignal_s { + BReactor *reactor; + BThreadSignal_handler handler; + int pipe[2]; + BFileDescriptor bfd; + DebugObject d_obj; +}; + +int BThreadSignal_Init (BThreadSignal *o, BReactor *reactor, BThreadSignal_handler handler) WARN_UNUSED; +void BThreadSignal_Free (BThreadSignal *o); +int BThreadSignal_Thread_Signal (BThreadSignal *o); + +#endif diff --git a/app/src/main/jni/badvpn/system/BTime.c b/app/src/main/jni/badvpn/system/BTime.c new file mode 100644 index 0000000..a1fa9e7 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BTime.c @@ -0,0 +1,38 @@ +/** + * @file BTime.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef BADVPN_PLUGIN +struct _BTime_global btime_global = { + #ifndef NDEBUG + 0 + #endif +}; +#endif diff --git a/app/src/main/jni/badvpn/system/BTime.h b/app/src/main/jni/badvpn/system/BTime.h new file mode 100644 index 0000000..6998814 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BTime.h @@ -0,0 +1,163 @@ +/** + * @file BTime.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * System time abstraction used by {@link BReactor}. + */ + +#ifndef BADVPN_SYSTEM_BTIME_H +#define BADVPN_SYSTEM_BTIME_H + +#if defined(BADVPN_USE_WINAPI) +#include +#elif defined(BADVPN_EMSCRIPTEN) +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include + +#include + +typedef int64_t btime_t; + +#define BTIME_MIN INT64_MIN + +struct _BTime_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + #if defined(BADVPN_USE_WINAPI) + LARGE_INTEGER start_time; + #elif defined(BADVPN_EMSCRIPTEN) + btime_t start_time; + #else + btime_t start_time; + int use_gettimeofday; + #endif +}; + +extern struct _BTime_global btime_global; + +static void BTime_Init (void) +{ + ASSERT(!btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + ASSERT_FORCE(QueryPerformanceCounter(&btime_global.start_time)) + + #elif defined(BADVPN_EMSCRIPTEN) + + btime_global.start_time = emscripten_get_now(); + + #else + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { + BLog(BLOG_WARNING, "CLOCK_MONOTONIC is not available. Timers will be confused by clock changes."); + + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + + btime_global.start_time = (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000; + btime_global.use_gettimeofday = 1; + } else { + btime_global.start_time = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000; + btime_global.use_gettimeofday = 0; + } + + #endif + + #ifndef NDEBUG + btime_global.initialized = 1; + #endif +} + +static btime_t btime_gettime (void) +{ + ASSERT(btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + LARGE_INTEGER count; + LARGE_INTEGER freq; + ASSERT_FORCE(QueryPerformanceCounter(&count)) + ASSERT_FORCE(QueryPerformanceFrequency(&freq)) + return (((count.QuadPart - btime_global.start_time.QuadPart) * 1000) / freq.QuadPart); + + #elif defined(BADVPN_EMSCRIPTEN) + + return (btime_t)emscripten_get_now() - btime_global.start_time; + + #else + + if (btime_global.use_gettimeofday) { + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + return ((int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000); + } else { + struct timespec ts; + ASSERT_FORCE(clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (((int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000) - btime_global.start_time); + } + + #endif +} + +static btime_t btime_add (btime_t t1, btime_t t2) +{ + // handle overflow + int overflows = add_int64_overflows(t1, t2); + btime_t sum; + if (overflows != 0) { + if (overflows > 0) { + sum = INT64_MAX; + } else { + sum = INT64_MIN; + } + } else { + sum = t1 + t2; + } + + return sum; +} + +static btime_t btime_getpast (void) +{ + return INT64_MIN; +} + +#endif diff --git a/app/src/main/jni/badvpn/system/BUnixSignal.c b/app/src/main/jni/badvpn/system/BUnixSignal.c new file mode 100644 index 0000000..94edd6b --- /dev/null +++ b/app/src/main/jni/badvpn/system/BUnixSignal.c @@ -0,0 +1,406 @@ +/** + * @file BUnixSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_SIGNALFD +#include +#endif + +#include +#include +#include + +#include + +#include + +#define BUNIXSIGNAL_MAX_SIGNALS 64 + +#ifdef BADVPN_USE_SIGNALFD + +static void signalfd_handler (BUnixSignal *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // read a signal + struct signalfd_siginfo siginfo; + int bytes = read(o->signalfd_fd, &siginfo, sizeof(siginfo)); + if (bytes < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + ASSERT_FORCE(bytes == sizeof(siginfo)) + + // check signal + if (siginfo.ssi_signo > INT_MAX) { + BLog(BLOG_ERROR, "read returned out of int range signo (%"PRIu32")", siginfo.ssi_signo); + return; + } + int signo = siginfo.ssi_signo; + if (sigismember(&o->signals, signo) <= 0) { + BLog(BLOG_ERROR, "read returned wrong signo (%d)", signo); + return; + } + + BLog(BLOG_DEBUG, "dispatching signal %d", signo); + + // call handler + o->handler(o->user, signo); + return; +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags, intptr_t data) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // call signal + o->handler(o->user, entry->signo); + return; +} + +#endif + +#ifdef BADVPN_USE_SELFPIPE + +struct BUnixSignal_selfpipe_entry *bunixsignal_selfpipe_entries[BUNIXSIGNAL_MAX_SIGNALS]; + +static void free_selfpipe_entry (struct BUnixSignal_selfpipe_entry *entry) +{ + BUnixSignal *o = entry->parent; + + // uninstall signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + ASSERT_FORCE(sigaction(entry->signo, &act, NULL) == 0) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + + // close pipe + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) +} + +static void pipe_read_fd_handler (struct BUnixSignal_selfpipe_entry *entry, int events) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // read a byte + uint8_t b; + if (read(entry->pipefds[0], &b, sizeof(b)) < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + + // call handler + o->handler(o->user, entry->signo); + return; +} + +static void signal_handler (int signo) +{ + ASSERT(signo >= 0) + ASSERT(signo < BUNIXSIGNAL_MAX_SIGNALS) + + struct BUnixSignal_selfpipe_entry *entry = bunixsignal_selfpipe_entries[signo]; + + uint8_t b = 0; + write(entry->pipefds[1], &b, sizeof(b)); +} + +#endif + +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->signals = signals; + o->handler = handler; + o->user = user; + + #ifdef BADVPN_USE_SIGNALFD + + // init signalfd fd + if ((o->signalfd_fd = signalfd(-1, &o->signals, 0)) < 0) { + BLog(BLOG_ERROR, "signalfd failed"); + goto fail0; + } + + // set non-blocking + if (fcntl(o->signalfd_fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init signalfd BFileDescriptor + BFileDescriptor_Init(&o->signalfd_bfd, o->signalfd_fd, (BFileDescriptor_handler)signalfd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->signalfd_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->signalfd_bfd, BREACTOR_READ); + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // count signals + int num_signals = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init kevents + o->num_entries = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + struct BUnixSignal_kevent_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + if (!BReactorKEvent_Init(&entry->kevent, o->reactor, (BReactorKEvent_handler)kevent_handler, entry, entry->signo, EVFILT_SIGNAL, 0, 0)) { + BLog(BLOG_ERROR, "BReactorKEvent_Init failed"); + goto fail2; + } + o->num_entries++; + } + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + // count signals + int num_signals = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init entries + o->num_entries = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + + struct BUnixSignal_selfpipe_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + + // init pipe + if (pipe(entry->pipefds) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto loop_fail0; + } + + // set pipe ends non-blocking + if (!badvpn_set_nonblocking(entry->pipefds[0]) || !badvpn_set_nonblocking(entry->pipefds[1])) { + BLog(BLOG_ERROR, "set nonblocking failed"); + goto loop_fail1; + } + + // init read end BFileDescriptor + BFileDescriptor_Init(&entry->pipe_read_bfd, entry->pipefds[0], (BFileDescriptor_handler)pipe_read_fd_handler, entry); + if (!BReactor_AddFileDescriptor(o->reactor, &entry->pipe_read_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto loop_fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &entry->pipe_read_bfd, BREACTOR_READ); + + // set global entry pointer + bunixsignal_selfpipe_entries[entry->signo] = entry; + + // install signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + sigemptyset(&act.sa_mask); + if (sigaction(entry->signo, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto loop_fail2; + } + + o->num_entries++; + + continue; + + loop_fail2: + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + loop_fail1: + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) + loop_fail0: + goto fail2; + } + + #endif + + DebugObject_Init(&o->d_obj); + + return 1; + + #ifdef BADVPN_USE_SIGNALFD +fail2: + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); +fail1: + ASSERT_FORCE(close(o->signalfd_fd) == 0) + #endif + + #ifdef BADVPN_USE_KEVENT +fail2: + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + BFree(o->entries); + #endif + + #ifdef BADVPN_USE_SELFPIPE +fail2: + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + BFree(o->entries); + #endif + +fail0: + return 0; +} + +void BUnixSignal_Free (BUnixSignal *o, int unblock) +{ + ASSERT(unblock == 0 || unblock == 1) + DebugObject_Free(&o->d_obj); + + #ifdef BADVPN_USE_SIGNALFD + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free signalfd BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); + + // free signalfd fd + ASSERT_FORCE(close(o->signalfd_fd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free kevents + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + if (!unblock) { + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + } + } + + // free entries + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif +} diff --git a/app/src/main/jni/badvpn/system/BUnixSignal.h b/app/src/main/jni/badvpn/system/BUnixSignal.h new file mode 100644 index 0000000..2438be9 --- /dev/null +++ b/app/src/main/jni/badvpn/system/BUnixSignal.h @@ -0,0 +1,132 @@ +/** + * @file BUnixSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for catching unix signals. + */ + +#ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H +#define BADVPN_SYSTEM_BUNIXSIGNAL_H + +#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_SELFPIPE)) != 1 +#error Unknown signal backend or too many signal backends +#endif + +#include +#include + +#include +#include +#include + +struct BUnixSignal_s; + +/** + * Handler function called when a signal is received. + * + * @param user as in {@link BUnixSignal_Init} + * @param signo signal number. Will be one of the signals provided to {@link signals}. + */ +typedef void (*BUnixSignal_handler) (void *user, int signo); + +#ifdef BADVPN_USE_KEVENT +struct BUnixSignal_kevent_entry { + struct BUnixSignal_s *parent; + int signo; + BReactorKEvent kevent; +}; +#endif + +#ifdef BADVPN_USE_SELFPIPE +struct BUnixSignal_selfpipe_entry { + struct BUnixSignal_s *parent; + int signo; + int pipefds[2]; + BFileDescriptor pipe_read_bfd; +}; +#endif + +/** + * Object for catching unix signals. + */ +typedef struct BUnixSignal_s { + BReactor *reactor; + sigset_t signals; + BUnixSignal_handler handler; + void *user; + + #ifdef BADVPN_USE_SIGNALFD + int signalfd_fd; + BFileDescriptor signalfd_bfd; + #endif + + #ifdef BADVPN_USE_KEVENT + struct BUnixSignal_kevent_entry *entries; + int num_entries; + #endif + + #ifdef BADVPN_USE_SELFPIPE + struct BUnixSignal_selfpipe_entry *entries; + int num_entries; + #endif + + DebugObject d_obj; +} BUnixSignal; + +/** + * Initializes the object. + * {@link BLog_Init} must have been done. + * + * WARNING: for every signal number there should be at most one {@link BUnixSignal} + * object handling it (or anything else that could interfere). + * + * This blocks the signal using sigprocmask() and sets up signalfd() for receiving + * signals. + * + * @param o the object + * @param reactor reactor we live in + * @param signals signals to handle. See man 3 sigsetops. + * @param handler handler function to call when a signal is received + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + * @param unblock whether to unblock the signals using sigprocmask(). Not unblocking it + * can be used while the program is exiting gracefully to prevent the + * signals from being handled handled according to its default disposition + * after this function is called. Must be 0 or 1. + */ +void BUnixSignal_Free (BUnixSignal *o, int unblock); + +#endif diff --git a/app/src/main/jni/badvpn/system/CMakeLists.txt b/app/src/main/jni/badvpn/system/CMakeLists.txt new file mode 100644 index 0000000..99f7333 --- /dev/null +++ b/app/src/main/jni/badvpn/system/CMakeLists.txt @@ -0,0 +1,44 @@ +set(BSYSTEM_ADDITIONAL_LIBS) +set(BSYSTEM_ADDITIONAL_SOURCES) + +if (NOT EMSCRIPTEN) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BSignal.c + BNetwork.c + ) + + if (WIN32) + list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32 mswsock) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BConnection_win.c + BDatagram_win.c + ) + endif () + + if (NOT WIN32) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BUnixSignal.c + BConnection_unix.c + BDatagram_unix.c + BProcess.c + BInputProcess.c + BThreadSignal.c + BLockReactor.c + ) + endif () +endif () + +if (BREACTOR_BACKEND STREQUAL "badvpn") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_badvpn.c) +elseif (BREACTOR_BACKEND STREQUAL "glib") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_glib.c) + list(APPEND BSYSTEM_ADDITIONAL_LIBS ${GLIB2_LIBRARIES}) +elseif (BREACTOR_BACKEND STREQUAL "emscripten") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_emscripten.c) +endif () + +set(SYSTEM_SOURCES + BTime.c + ${BSYSTEM_ADDITIONAL_SOURCES} +) +badvpn_add_library(system "base;flow" "${BSYSTEM_ADDITIONAL_LIBS}" "${SYSTEM_SOURCES}") diff --git a/app/src/main/jni/badvpn/tests/CMakeLists.txt b/app/src/main/jni/badvpn/tests/CMakeLists.txt new file mode 100644 index 0000000..ebb0d62 --- /dev/null +++ b/app/src/main/jni/badvpn/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(chunkbuffer2_test chunkbuffer2_test.c) + +add_executable(bproto_test bproto_test.c) + +if (BUILDING_THREADWORK) + add_executable(threadwork_test threadwork_test.c) + target_link_libraries(threadwork_test threadwork) +endif () diff --git a/app/src/main/jni/badvpn/tests/bproto_test.bproto b/app/src/main/jni/badvpn/tests/bproto_test.bproto new file mode 100644 index 0000000..4641a74 --- /dev/null +++ b/app/src/main/jni/badvpn/tests/bproto_test.bproto @@ -0,0 +1,9 @@ +message msg1 { + required uint16 a = 5; + optional uint32 b = 6; + required repeated uint64 c = 7; + repeated uint16 d = 8; + required uint8 e = 9; + required data f = 10; + required data("4") g = 11; +}; diff --git a/app/src/main/jni/badvpn/tests/bproto_test.c b/app/src/main/jni/badvpn/tests/bproto_test.c new file mode 100644 index 0000000..067695b --- /dev/null +++ b/app/src/main/jni/badvpn/tests/bproto_test.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include +#include + +#include + +int main () +{ + uint16_t a = 17501; + uint64_t c = 82688926; + uint16_t d1 = 1517; + uint16_t d2 = 1518; + uint8_t e = 72; + const char *f = "hello world"; + const char *g = "helo"; + + // encode message + + int len = msg1_SIZEa + msg1_SIZEc + msg1_SIZEd + msg1_SIZEd + msg1_SIZEe + msg1_SIZEf(strlen(f)) + msg1_SIZEg; + + uint8_t *msg = (uint8_t *)BAlloc(len); + ASSERT_FORCE(msg) + msg1Writer writer; + msg1Writer_Init(&writer, msg); + msg1Writer_Adda(&writer, a); + msg1Writer_Addc(&writer, c); + msg1Writer_Addd(&writer, d1); + msg1Writer_Addd(&writer, d2); + msg1Writer_Adde(&writer, e); + uint8_t *f_dst = msg1Writer_Addf(&writer, strlen(f)); + memcpy(f_dst, f, strlen(f)); + uint8_t *g_dst = msg1Writer_Addg(&writer); + memcpy(g_dst, g, strlen(g)); + int len2 = msg1Writer_Finish(&writer); + ASSERT_EXECUTE(len2 == len) + + // parse message + + msg1Parser parser; + ASSERT_EXECUTE(msg1Parser_Init(&parser, msg, len)) + + // check parse results + + uint16_t p_a; + uint64_t p_c; + uint16_t p_d1; + uint16_t p_d2; + uint8_t p_e; + uint8_t *p_f; + int p_f_len; + uint8_t *p_g; + ASSERT_EXECUTE(msg1Parser_Geta(&parser, &p_a)) + ASSERT_EXECUTE(msg1Parser_Getc(&parser, &p_c)) + ASSERT_EXECUTE(msg1Parser_Getd(&parser, &p_d1)) + ASSERT_EXECUTE(msg1Parser_Getd(&parser, &p_d2)) + ASSERT_EXECUTE(msg1Parser_Gete(&parser, &p_e)) + ASSERT_EXECUTE(msg1Parser_Getf(&parser, &p_f, &p_f_len)) + ASSERT_EXECUTE(msg1Parser_Getg(&parser, &p_g)) + + ASSERT(p_a == a) + ASSERT(p_c == c) + ASSERT(p_d1 == d1) + ASSERT(p_d2 == d2) + ASSERT(p_e == e) + ASSERT(p_f_len == strlen(f) && !memcmp(p_f, f, p_f_len)) + ASSERT(!memcmp(p_g, g, strlen(g))) + + ASSERT(msg1Parser_GotEverything(&parser)) + + BFree(msg); + + return 0; +} diff --git a/app/src/main/jni/badvpn/tests/chunkbuffer2_test.c b/app/src/main/jni/badvpn/tests/chunkbuffer2_test.c new file mode 100644 index 0000000..ff94bb3 --- /dev/null +++ b/app/src/main/jni/badvpn/tests/chunkbuffer2_test.c @@ -0,0 +1,86 @@ +#include +#include + +int main () +{ + struct ChunkBuffer2_block blocks[16]; + ChunkBuffer2 buf; + ChunkBuffer2_Init(&buf, blocks, 16, 4 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 15 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == NULL) + ASSERT_FORCE(buf.output_avail == -1) + + ChunkBuffer2_SubmitPacket(&buf, sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[3]) + ASSERT_FORCE(buf.input_avail == 13 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 8 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.input_avail == 4 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 4 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == NULL) + ASSERT_FORCE(buf.input_avail == -1) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 1 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[3]) + ASSERT_FORCE(buf.output_avail == 8 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 10 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.output_avail == 4 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 9 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.input_avail == 0 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.output_avail == 4 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.input_avail == 5 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 9 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 1 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == NULL) + ASSERT_FORCE(buf.input_avail == -1) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 9 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 9 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 15 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == NULL) + ASSERT_FORCE(buf.output_avail == -1) + + return 0; +} diff --git a/app/src/main/jni/badvpn/tests/threadwork_test.c b/app/src/main/jni/badvpn/tests/threadwork_test.c new file mode 100644 index 0000000..9c18b4b --- /dev/null +++ b/app/src/main/jni/badvpn/tests/threadwork_test.c @@ -0,0 +1,87 @@ +#include +#include + +#include +#include +#include +#include + +BReactor reactor; +BThreadWorkDispatcher twd; +BThreadWork tw1; +BThreadWork tw2; +BThreadWork tw3; +int num_left; + +static void handler_done (void *user) +{ + printf("work done\n"); + + num_left--; + + if (num_left == 0) { + printf("all works done, quitting\n"); + BReactor_Quit(&reactor, 0); + } +} + +static void work_func (void *user) +{ + unsigned int x = 0; + + for (int i = 0; i < 10000; i++) { + for (int j = 0; j < 10000; j++) { + x++; + } + } +} + +static void dummy_works (int n) +{ + for (int i = 0; i < n; i++) { + BThreadWork tw_tmp; + BThreadWork_Init(&tw_tmp, &twd, handler_done, NULL, work_func, NULL); + BThreadWork_Free(&tw_tmp); + } +} + +int main () +{ + BLog_InitStdout(); + BLog_SetChannelLoglevel(BLOG_CHANNEL_BThreadWork, BLOG_DEBUG); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BThreadWorkDispatcher_Init(&twd, &reactor, 1)) { + DEBUG("BThreadWorkDispatcher_Init failed"); + goto fail2; + } + + dummy_works(200); + + BThreadWork_Init(&tw1, &twd, handler_done, NULL, work_func, NULL); + + BThreadWork_Init(&tw2, &twd, handler_done, NULL, work_func, NULL); + + BThreadWork_Init(&tw3, &twd, handler_done, NULL, work_func, NULL); + + dummy_works(200); + + num_left = 3; + + BReactor_Exec(&reactor); + + BThreadWork_Free(&tw3); + BThreadWork_Free(&tw2); + BThreadWork_Free(&tw1); + BThreadWorkDispatcher_Free(&twd); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); + DebugObjectGlobal_Finish(); + return 0; +} diff --git a/app/src/main/jni/badvpn/threadwork/BThreadWork.c b/app/src/main/jni/badvpn/threadwork/BThreadWork.c new file mode 100644 index 0000000..3c29f95 --- /dev/null +++ b/app/src/main/jni/badvpn/threadwork/BThreadWork.c @@ -0,0 +1,451 @@ +/** + * @file BThreadWork.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include + #include + #include +#endif + +#include +#include + +#include + +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + +static void * dispatcher_thread (struct BThreadWorkDispatcher_thread *t) +{ + BThreadWorkDispatcher *o = t->d; + + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + while (1) { + // exit if requested + if (o->cancel) { + break; + } + + if (LinkedList1_IsEmpty(&o->pending_list)) { + // wait for event + ASSERT_FORCE(pthread_cond_wait(&t->new_cond, &o->mutex) == 0) + continue; + } + + // grab the work + BThreadWork *w = UPPER_OBJECT(LinkedList1_GetFirst(&o->pending_list), BThreadWork, list_node); + ASSERT(w->state == BTHREADWORK_STATE_PENDING) + LinkedList1_Remove(&o->pending_list, &w->list_node); + t->running_work = w; + w->state = BTHREADWORK_STATE_RUNNING; + + // do the work + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + w->work_func(w->work_func_user); + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + // release the work + t->running_work = NULL; + LinkedList1_Append(&o->finished_list, &w->list_node); + w->state = BTHREADWORK_STATE_FINISHED; + ASSERT_FORCE(sem_post(&w->finished_sem) == 0) + + // write to pipe + uint8_t b = 0; + int res = write(o->pipe[1], &b, sizeof(b)); + if (res < 0) { + int error = errno; + ASSERT_FORCE(error == EAGAIN || error == EWOULDBLOCK) + } + } + + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + return NULL; +} + +static void dispatch_job (BThreadWorkDispatcher *o) +{ + ASSERT(o->num_threads > 0) + + // lock + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + // check for finished job + if (LinkedList1_IsEmpty(&o->finished_list)) { + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + return; + } + + // grab finished job + BThreadWork *w = UPPER_OBJECT(LinkedList1_GetFirst(&o->finished_list), BThreadWork, list_node); + ASSERT(w->state == BTHREADWORK_STATE_FINISHED) + LinkedList1_Remove(&o->finished_list, &w->list_node); + + // schedule more + if (!LinkedList1_IsEmpty(&o->finished_list)) { + BPending_Set(&o->more_job); + } + + // set state forgotten + w->state = BTHREADWORK_STATE_FORGOTTEN; + + // unlock + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + // call handler + w->handler_done(w->user); + return; +} + +static void pipe_fd_handler (BThreadWorkDispatcher *o, int events) +{ + ASSERT(o->num_threads > 0) + DebugObject_Access(&o->d_obj); + + // read data from pipe + uint8_t b[64]; + int res = read(o->pipe[0], b, sizeof(b)); + if (res < 0) { + int error = errno; + ASSERT_FORCE(error == EAGAIN || error == EWOULDBLOCK) + } else { + ASSERT(res > 0) + } + + dispatch_job(o); + return; +} + +static void more_job_handler (BThreadWorkDispatcher *o) +{ + ASSERT(o->num_threads > 0) + DebugObject_Access(&o->d_obj); + + dispatch_job(o); + return; +} + +static void stop_threads (BThreadWorkDispatcher *o) +{ + // set cancelling + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + o->cancel = 1; + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + while (o->num_threads > 0) { + struct BThreadWorkDispatcher_thread *t = &o->threads[o->num_threads - 1]; + + // wake up thread + ASSERT_FORCE(pthread_cond_signal(&t->new_cond) == 0) + + // wait for thread to exit + ASSERT_FORCE(pthread_join(t->thread, NULL) == 0) + + // free condition variable + ASSERT_FORCE(pthread_cond_destroy(&t->new_cond) == 0) + + o->num_threads--; + } +} + +#endif + +static void work_job_handler (BThreadWork *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + ASSERT(o->d->num_threads == 0) + #endif + DebugObject_Access(&o->d_obj); + + // do the work + o->work_func(o->work_func_user); + + // call handler + o->handler_done(o->user); + return; +} + +int BThreadWorkDispatcher_Init (BThreadWorkDispatcher *o, BReactor *reactor, int num_threads_hint) +{ + // init arguments + o->reactor = reactor; + + if (num_threads_hint < 0) { + num_threads_hint = 2; + } + if (num_threads_hint > BTHREADWORK_MAX_THREADS) { + num_threads_hint = BTHREADWORK_MAX_THREADS; + } + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + if (num_threads_hint > 0) { + // init pending list + LinkedList1_Init(&o->pending_list); + + // init finished list + LinkedList1_Init(&o->finished_list); + + // init mutex + if (pthread_mutex_init(&o->mutex, NULL) != 0) { + BLog(BLOG_ERROR, "pthread_mutex_init failed"); + goto fail0; + } + + // init pipe + if (pipe(o->pipe) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail1; + } + + // set read end non-blocking + if (fcntl(o->pipe[0], F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "fcntl failed"); + goto fail2; + } + + // set write end non-blocking + if (fcntl(o->pipe[1], F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "fcntl failed"); + goto fail2; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->pipe[0], (BFileDescriptor_handler)pipe_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init more job + BPending_Init(&o->more_job, BReactor_PendingGroup(o->reactor), (BPending_handler)more_job_handler, o); + + // set not cancelling + o->cancel = 0; + + // init threads + o->num_threads = 0; + for (int i = 0; i < num_threads_hint; i++) { + struct BThreadWorkDispatcher_thread *t = &o->threads[i]; + + // set parent pointer + t->d = o; + + // set no running work + t->running_work = NULL; + + // init condition variable + if (pthread_cond_init(&t->new_cond, NULL) != 0) { + BLog(BLOG_ERROR, "pthread_cond_init failed"); + goto fail3; + } + + // init thread + if (pthread_create(&t->thread, NULL, (void * (*) (void *))dispatcher_thread, t) != 0) { + BLog(BLOG_ERROR, "pthread_create failed"); + ASSERT_FORCE(pthread_cond_destroy(&t->new_cond) == 0) + goto fail3; + } + + o->num_threads++; + } + } + + #endif + + DebugObject_Init(&o->d_obj); + DebugCounter_Init(&o->d_ctr); + return 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD +fail3: + stop_threads(o); + BPending_Free(&o->more_job); + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); +fail2: + ASSERT_FORCE(close(o->pipe[0]) == 0) + ASSERT_FORCE(close(o->pipe[1]) == 0) +fail1: + ASSERT_FORCE(pthread_mutex_destroy(&o->mutex) == 0) +fail0: + return 0; + #endif +} + +void BThreadWorkDispatcher_Free (BThreadWorkDispatcher *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (o->num_threads > 0) { + ASSERT(LinkedList1_IsEmpty(&o->pending_list)) + for (int i = 0; i < o->num_threads; i++) { ASSERT(!o->threads[i].running_work) } + ASSERT(LinkedList1_IsEmpty(&o->finished_list)) + } + #endif + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + if (o->num_threads > 0) { + // stop threads + stop_threads(o); + + // free more job + BPending_Free(&o->more_job); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free pipe + ASSERT_FORCE(close(o->pipe[0]) == 0) + ASSERT_FORCE(close(o->pipe[1]) == 0) + + // free mutex + ASSERT_FORCE(pthread_mutex_destroy(&o->mutex) == 0) + } + + #endif +} + +int BThreadWorkDispatcher_UsingThreads (BThreadWorkDispatcher *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + return (o->num_threads > 0); + #else + return 0; + #endif +} + +void BThreadWork_Init (BThreadWork *o, BThreadWorkDispatcher *d, BThreadWork_handler_done handler_done, void *user, BThreadWork_work_func work_func, void *work_func_user) +{ + DebugObject_Access(&d->d_obj); + + // init arguments + o->d = d; + o->handler_done = handler_done; + o->user = user; + o->work_func = work_func; + o->work_func_user = work_func_user; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (d->num_threads > 0) { + // set state + o->state = BTHREADWORK_STATE_PENDING; + + // init finished semaphore + ASSERT_FORCE(sem_init(&o->finished_sem, 0, 0) == 0) + + // post work + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + LinkedList1_Append(&d->pending_list, &o->list_node); + for (int i = 0; i < d->num_threads; i++) { + if (!d->threads[i].running_work) { + ASSERT_FORCE(pthread_cond_signal(&d->threads[i].new_cond) == 0) + break; + } + } + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + } else { + #endif + // schedule job + BPending_Init(&o->job, BReactor_PendingGroup(d->reactor), (BPending_handler)work_job_handler, o); + BPending_Set(&o->job); + #ifdef BADVPN_THREADWORK_USE_PTHREAD + } + #endif + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&d->d_ctr); +} + +void BThreadWork_Free (BThreadWork *o) +{ + BThreadWorkDispatcher *d = o->d; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&d->d_ctr); + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (d->num_threads > 0) { + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + + switch (o->state) { + case BTHREADWORK_STATE_PENDING: { + BLog(BLOG_DEBUG, "remove pending work"); + + // remove from pending list + LinkedList1_Remove(&d->pending_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_RUNNING: { + BLog(BLOG_DEBUG, "remove running work"); + + // wait for the work to finish running + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + ASSERT_FORCE(sem_wait(&o->finished_sem) == 0) + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + + ASSERT(o->state == BTHREADWORK_STATE_FINISHED) + + // remove from finished list + LinkedList1_Remove(&d->finished_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_FINISHED: { + BLog(BLOG_DEBUG, "remove finished work"); + + // remove from finished list + LinkedList1_Remove(&d->finished_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_FORGOTTEN: { + BLog(BLOG_DEBUG, "remove forgotten work"); + } break; + + default: + ASSERT(0); + } + + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + + // free finished semaphore + ASSERT_FORCE(sem_destroy(&o->finished_sem) == 0) + } else { + #endif + BPending_Free(&o->job); + #ifdef BADVPN_THREADWORK_USE_PTHREAD + } + #endif +} diff --git a/app/src/main/jni/badvpn/threadwork/BThreadWork.h b/app/src/main/jni/badvpn/threadwork/BThreadWork.h new file mode 100644 index 0000000..e29c080 --- /dev/null +++ b/app/src/main/jni/badvpn/threadwork/BThreadWork.h @@ -0,0 +1,171 @@ +/** + * @file BThreadWork.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * System for performing computations (possibly) in parallel with the event loop + * in a different thread. + */ + +#ifndef BADVPN_BTHREADWORK_BTHREADWORK_H +#define BADVPN_BTHREADWORK_BTHREADWORK_H + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include + #include +#endif + +#include +#include +#include +#include + +#define BTHREADWORK_STATE_PENDING 1 +#define BTHREADWORK_STATE_RUNNING 2 +#define BTHREADWORK_STATE_FINISHED 3 +#define BTHREADWORK_STATE_FORGOTTEN 4 + +#define BTHREADWORK_MAX_THREADS 8 + +struct BThreadWork_s; +struct BThreadWorkDispatcher_s; + +/** + * Function called to do the work for a {@link BThreadWork}. + * The function may be called in another thread, in parallel with the event loop. + * + * @param user as work_func_user in {@link BThreadWork_Init} + */ +typedef void (*BThreadWork_work_func) (void *user); + +/** + * Handler called when a {@link BThreadWork} work is done. + * + * @param user as in {@link BThreadWork_Init} + */ +typedef void (*BThreadWork_handler_done) (void *user); + +#ifdef BADVPN_THREADWORK_USE_PTHREAD +struct BThreadWorkDispatcher_thread { + struct BThreadWorkDispatcher_s *d; + struct BThreadWork_s *running_work; + pthread_cond_t new_cond; + pthread_t thread; +}; +#endif + +typedef struct BThreadWorkDispatcher_s { + BReactor *reactor; + #ifdef BADVPN_THREADWORK_USE_PTHREAD + LinkedList1 pending_list; + LinkedList1 finished_list; + pthread_mutex_t mutex; + int pipe[2]; + BFileDescriptor bfd; + BPending more_job; + int cancel; + int num_threads; + struct BThreadWorkDispatcher_thread threads[BTHREADWORK_MAX_THREADS]; + #endif + DebugObject d_obj; + DebugCounter d_ctr; +} BThreadWorkDispatcher; + +typedef struct BThreadWork_s { + BThreadWorkDispatcher *d; + BThreadWork_handler_done handler_done; + void *user; + BThreadWork_work_func work_func; + void *work_func_user; + union { + #ifdef BADVPN_THREADWORK_USE_PTHREAD + struct { + LinkedList1Node list_node; + int state; + sem_t finished_sem; + }; + #endif + struct { + BPending job; + }; + }; + DebugObject d_obj; +} BThreadWork; + +/** + * Initializes the work dispatcher. + * Works may be started using {@link BThreadWork_Init}. + * + * @param o the object + * @param reactor reactor we live in + * @param num_threads_hint hint for the number of threads to use: + * <0 - A choice will be made automatically, probably based on the number of CPUs. + * 0 - No additional threads will be used, and computations will be performed directly + * in the event loop in job handlers. + * @return 1 on success, 0 on failure + */ +int BThreadWorkDispatcher_Init (BThreadWorkDispatcher *o, BReactor *reactor, int num_threads_hint) WARN_UNUSED; + +/** + * Frees the work dispatcher. + * There must be no {@link BThreadWork}'s with this dispatcher. + * + * @param o the object + */ +void BThreadWorkDispatcher_Free (BThreadWorkDispatcher *o); + +/** + * Determines whether threads are being used for computations, or computations + * are done in the event loop. + * + * @return 1 if threads are being used, 0 if not + */ +int BThreadWorkDispatcher_UsingThreads (BThreadWorkDispatcher *o); + +/** + * Initializes the work. + * + * @param o the object + * @param d work dispatcher + * @param handler_done handler to call when the work is done + * @param user argument to handler + * @param work_func function that will do the work, possibly from another thread + * @param work_func_user argument to work_func + */ +void BThreadWork_Init (BThreadWork *o, BThreadWorkDispatcher *d, BThreadWork_handler_done handler_done, void *user, BThreadWork_work_func work_func, void *work_func_user); + +/** + * Frees the work. + * After this function returns, the work function will either have fully executed, + * or not called at all, and never will be. + * + * @param o the object + */ +void BThreadWork_Free (BThreadWork *o); + +#endif diff --git a/app/src/main/jni/badvpn/threadwork/CMakeLists.txt b/app/src/main/jni/badvpn/threadwork/CMakeLists.txt new file mode 100644 index 0000000..d7e6412 --- /dev/null +++ b/app/src/main/jni/badvpn/threadwork/CMakeLists.txt @@ -0,0 +1,7 @@ +set(BADVPN_THREADWORK_EXTRA_LIBS) +if (BADVPN_THREADWORK_USE_PTHREAD) + list(APPEND BADVPN_THREADWORK_EXTRA_LIBS pthread) +endif () + +add_library(threadwork BThreadWork.c) +target_link_libraries(threadwork system ${BADVPN_THREADWORK_EXTRA_LIBS}) diff --git a/app/src/main/jni/badvpn/tun2socks/CMakeLists.txt b/app/src/main/jni/badvpn/tun2socks/CMakeLists.txt new file mode 100644 index 0000000..8c8597c --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(badvpn-tun2socks + tun2socks.c + SocksUdpGwClient.c +) +target_link_libraries(badvpn-tun2socks system flow tuntap lwip socksclient udpgw_client) + +install( + TARGETS badvpn-tun2socks + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-tun2socks.8 + DESTINATION share/man/man8 +) diff --git a/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c b/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c new file mode 100644 index 0000000..bb60188 --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#ifdef ANDROID_UDP + +#include +#define CONNECTION_UDP_BUFFER_SIZE 1 + +#else + +static void free_socks (SocksUdpGwClient *o); +static void try_connect (SocksUdpGwClient *o); +static void reconnect_timer_handler (SocksUdpGwClient *o); +static void socks_client_handler (SocksUdpGwClient *o, int event); +static void udpgw_handler_servererror (SocksUdpGwClient *o); +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +#endif + +#ifdef ANDROID_UDP +static void dgram_handler (SocksUdpGwClient_connection *o, int event); +static void dgram_handler_received (SocksUdpGwClient_connection *o, uint8_t *data, int data_len); +static int conaddr_comparator (void *unused, SocksUdpGwClient_conaddr *v1, SocksUdpGwClient_conaddr *v2); +static SocksUdpGwClient_connection * find_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr); +static SocksUdpGwClient_connection * reuse_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr); +static void connection_send (SocksUdpGwClient_connection *o, const uint8_t *data, int data_len); +static void connection_first_job_handler (SocksUdpGwClient_connection *con); +static SocksUdpGwClient_connection *connection_init (SocksUdpGwClient *client, SocksUdpGwClient_conaddr conaddr, const uint8_t *data, int data_len); +static void connection_free (SocksUdpGwClient_connection *o); + +static void dgram_handler (SocksUdpGwClient_connection *o, int event) +{ + SocksUdpGwClient *client = o->client; + ASSERT(client); + + DebugObject_Access(&client->d_obj); + + BLog(BLOG_INFO, "UDP error"); +} + +static void dgram_handler_received (SocksUdpGwClient_connection *o, uint8_t *data, int data_len) +{ + SocksUdpGwClient *client = o->client; + ASSERT(client); + + DebugObject_Access(&client->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= client->udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&o->udp_recv_if); + + // check header + if (data_len < sizeof(struct socks_udp_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct socks_udp_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t frag = header.frag; + uint8_t atyp = header.atyp; + + // check fragment + if (frag) { + BLog(BLOG_ERROR, "unexpected frag"); + return; + } + + // parse address + BAddr remote_addr; + if (atyp == SOCKS_ATYP_IPV6) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + BLog(BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + BLog(BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check remote addr + if (!BAddr_Compare(&remote_addr, &o->conaddr.remote_addr)) { + BLog(BLOG_ERROR, "remote addr not match"); + return; + } + + // check remaining data + if (data_len > client->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // submit to user + client->handler_received(client->user, o->conaddr.local_addr, remote_addr, data, data_len); +} + +static int conaddr_comparator (void *unused, SocksUdpGwClient_conaddr *v1, SocksUdpGwClient_conaddr *v2) +{ + int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); + if (r) { + return r; + } + return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); +} + +static SocksUdpGwClient_connection * find_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree, &conaddr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, SocksUdpGwClient_connection, connections_tree_node); +} + +static SocksUdpGwClient_connection * reuse_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr) +{ + ASSERT(!find_connection(o, conaddr)) + ASSERT(o->num_connections > 0) + + // get least recently used connection + SocksUdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), SocksUdpGwClient_connection, connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree, &con->connections_tree_node); + + // set new conaddr + con->conaddr = conaddr; + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree, &con->connections_tree_node, NULL)) + + return con; +} + +static void connection_send (SocksUdpGwClient_connection *o, const uint8_t *data, int data_len) +{ + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(&o->udp_send_writer, &out)) { + BLog(BLOG_ERROR, "out of UDP buffer"); + return; + } + int out_pos = 0; + + // write header + BAddr remote_addr = o->conaddr.remote_addr; + struct socks_udp_header header; + header.rsv = 0; + header.frag = 0; + if (remote_addr.type == BADDR_TYPE_IPV4) { + header.atyp = SOCKS_ATYP_IPV4; + } else { + header.atyp = SOCKS_ATYP_IPV6; + } + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (remote_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = remote_addr.ipv4.ip; + addr_ipv4.addr_port = remote_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = remote_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write packet to buffer + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit written message + BufferWriter_EndPacket(&o->udp_send_writer, out_pos); +} + +static void connection_first_job_handler (SocksUdpGwClient_connection *con) +{ + connection_send(con, con->first_data, con->first_data_len); +} + +static SocksUdpGwClient_connection *connection_init (SocksUdpGwClient *client, SocksUdpGwClient_conaddr conaddr, const uint8_t *data, int data_len) +{ + // allocate structure + SocksUdpGwClient_connection *o = (SocksUdpGwClient_connection *) malloc(sizeof(*o)); + if (!o) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail; + } + + // init arguments + o->client = client; + o->conaddr = conaddr; + o->first_data = data; + o->first_data_len = data_len; + + // init first job + BPending_Init(&o->first_job, BReactor_PendingGroup(client->reactor), (BPending_handler)connection_first_job_handler, o); + BPending_Set(&o->first_job); + + // init UDP dgram + if (!BDatagram_Init(&o->udp_dgram, client->socks_server_addr.type, client->reactor, o, (BDatagram_handler)dgram_handler)) { + goto fail0; + } + + // set SO_REUSEADDR + if (!BDatagram_SetReuseAddr(&o->udp_dgram, 1)) { + BLog(BLOG_ERROR, "set SO_REUSEADDR failed"); + goto fail1; + } + + // set UDP dgram send address + BIPAddr ipaddr; + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.type = client->socks_server_addr.type; + BDatagram_SetSendAddrs(&o->udp_dgram, client->socks_server_addr, ipaddr); + + // init UDP dgram interfaces + BDatagram_SendAsync_Init(&o->udp_dgram, client->udp_mtu); + BDatagram_RecvAsync_Init(&o->udp_dgram, client->udp_mtu); + + // init UDP writer + BufferWriter_Init(&o->udp_send_writer, client->udp_mtu, BReactor_PendingGroup(client->reactor)); + + // init UDP buffer + if (!PacketBuffer_Init(&o->udp_send_buffer, BufferWriter_GetOutput(&o->udp_send_writer), BDatagram_SendAsync_GetIf(&o->udp_dgram), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(client->reactor))) { + BLog(BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail2; + } + + // init UDP recv interface + PacketPassInterface_Init(&o->udp_recv_if, client->udp_mtu, (PacketPassInterface_handler_send)dgram_handler_received, o, BReactor_PendingGroup(client->reactor)); + + // init UDP recv buffer + if (!SinglePacketBuffer_Init(&o->udp_recv_buffer, BDatagram_RecvAsync_GetIf(&o->udp_dgram), &o->udp_recv_if, BReactor_PendingGroup(client->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail3; + } + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&client->connections_tree, &o->connections_tree_node, NULL)); + + // insert to connections list + LinkedList1_Append(&client->connections_list, &o->connections_list_node); + + // increment number of connections + client->num_connections++; + + // succeed to init + return o; + +fail3: + PacketPassInterface_Free(&o->udp_recv_if); + PacketBuffer_Free(&o->udp_send_buffer); +fail2: + BufferWriter_Free(&o->udp_send_writer); + BDatagram_RecvAsync_Free(&o->udp_dgram); + BDatagram_SendAsync_Free(&o->udp_dgram); +fail1: + BDatagram_Free(&o->udp_dgram); + +fail0: + BPending_Free(&o->first_job); + free(o); +fail: + return NULL; +} + +static void connection_free (SocksUdpGwClient_connection *o) +{ + SocksUdpGwClient *client = o->client; + + // decrement number of connections + client->num_connections--; + + // remove from connections list + LinkedList1_Remove(&client->connections_list, &o->connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&client->connections_tree, &o->connections_tree_node); + + // free UDP receive buffer + SinglePacketBuffer_Free(&o->udp_recv_buffer); + + // free UDP receive interface + PacketPassInterface_Free(&o->udp_recv_if); + + // free UDP buffer + PacketBuffer_Free(&o->udp_send_buffer); + + // free UDP writer + BufferWriter_Free(&o->udp_send_writer); + + // free UDP dgram interfaces + BDatagram_RecvAsync_Free(&o->udp_dgram); + BDatagram_SendAsync_Free(&o->udp_dgram); + + // free UDP dgram + BDatagram_Free(&o->udp_dgram); + + // free structure + free(o); +} + +#else + +static void free_socks (SocksUdpGwClient *o) +{ + ASSERT(o->have_socks) + + // disconnect udpgw client from SOCKS + if (o->socks_up) { + UdpGwClient_DisconnectServer(&o->udpgw_client); + } + + // free SOCKS client + BSocksClient_Free(&o->socks_client); + + // set have no SOCKS + o->have_socks = 0; +} + +static void try_connect (SocksUdpGwClient *o) +{ + ASSERT(!o->have_socks) + ASSERT(!BTimer_IsRunning(&o->reconnect_timer)) + + // init SOCKS client + if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { + BLog(BLOG_ERROR, "BSocksClient_Init failed"); + goto fail0; + } + + // set have SOCKS + o->have_socks = 1; + + // set SOCKS not up + o->socks_up = 0; + + return; + +fail0: + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void reconnect_timer_handler (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_socks) + + // try connecting + try_connect(o); +} + +static void socks_client_handler (SocksUdpGwClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + + switch (event) { + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!o->socks_up) + + BLog(BLOG_INFO, "SOCKS up"); + + // connect udpgw client to SOCKS + if (!UdpGwClient_ConnectServer(&o->udpgw_client, BSocksClient_GetSendInterface(&o->socks_client), BSocksClient_GetRecvInterface(&o->socks_client))) { + BLog(BLOG_ERROR, "UdpGwClient_ConnectServer failed"); + goto fail0; + } + + // set SOCKS up + o->socks_up = 1; + + return; + + fail0: + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + case BSOCKSCLIENT_EVENT_ERROR: + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + BLog(BLOG_INFO, "SOCKS error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + default: ASSERT(0); + } +} + +static void udpgw_handler_servererror (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + ASSERT(o->socks_up) + + BLog(BLOG_ERROR, "client reports server error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // submit to user + o->handler_received(o->user, local_addr, remote_addr, data, data_len); + return; +} + +#endif + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) +{ + // see asserts in UdpGwClient_Init + ASSERT(!BAddr_IsInvalid(&socks_server_addr)) +#ifndef ANDROID_UDP + ASSERT(remote_udpgw_addr.type == BADDR_TYPE_IPV4 || remote_udpgw_addr.type == BADDR_TYPE_IPV6) +#endif + + // init arguments + o->udp_mtu = udp_mtu; + o->socks_server_addr = socks_server_addr; + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->remote_udpgw_addr = remote_udpgw_addr; + o->reactor = reactor; + o->user = user; + o->handler_received = handler_received; + +#ifdef ANDROID_UDP + // compute MTUs + o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); + o->max_connections = max_connections; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // init connections tree by conaddr + BAVL_Init(&o->connections_tree, OFFSET_DIFF(SocksUdpGwClient_connection, conaddr, connections_tree_node), (BAVL_comparator)conaddr_comparator, NULL); + + // init connections list + LinkedList1_Init(&o->connections_list); +#else + // init udpgw client + if (!UdpGwClient_Init(&o->udpgw_client, udp_mtu, max_connections, send_buffer_size, keepalive_time, o->reactor, o, + (UdpGwClient_handler_servererror)udpgw_handler_servererror, + (UdpGwClient_handler_received)udpgw_handler_received + )) { + goto fail0; + } + + // init reconnect timer + BTimer_Init(&o->reconnect_timer, reconnect_time, (BTimer_handler)reconnect_timer_handler, o); + + // set have no SOCKS + o->have_socks = 0; + + // try connecting + try_connect(o); +#endif + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void SocksUdpGwClient_Free (SocksUdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + +#ifdef ANDROID_UDP + // free connections + while (!LinkedList1_IsEmpty(&o->connections_list)) { + SocksUdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), SocksUdpGwClient_connection, connections_list_node); + connection_free(con); + } +#else + // free SOCKS + if (o->have_socks) { + free_socks(o); + } + + // free reconnect timer + BReactor_RemoveTimer(o->reactor, &o->reconnect_timer); + + // free udpgw client + UdpGwClient_Free(&o->udpgw_client); +#endif +} + +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + // see asserts in UdpGwClient_SubmitPacket + +#ifdef ANDROID_UDP + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // build conaddr + SocksUdpGwClient_conaddr conaddr; + conaddr.local_addr = local_addr; + conaddr.remote_addr = remote_addr; + + // lookup connection + SocksUdpGwClient_connection *con = find_connection(o, conaddr); + + // if no connection and can't create a new one, reuse the least recently used une + if (!con && o->num_connections == o->max_connections) { + con = reuse_connection(o, conaddr); + } + + if (!con) { + // create new connection + con = connection_init(o, conaddr, data, data_len); + } else { + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // send packet to existing connection + connection_send(con, data, data_len); + } +#else + // submit to udpgw client + UdpGwClient_SubmitPacket(&o->udpgw_client, local_addr, remote_addr, is_dns, data, data_len); +#endif +} + diff --git a/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h b/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h new file mode 100644 index 0000000..ebb7902 --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H +#define BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H + +#include +#include +#include +#ifdef ANDROID_UDP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif + +typedef void (*SocksUdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +typedef struct { + int udp_mtu; + BAddr socks_server_addr; + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr remote_udpgw_addr; + BReactor *reactor; + void *user; + SocksUdpGwClient_handler_received handler_received; +#ifdef ANDROID_UDP + int udpgw_mtu; + int num_connections; + int max_connections; + BAVL connections_tree; + LinkedList1 connections_list; +#else + UdpGwClient udpgw_client; + BTimer reconnect_timer; + int have_socks; + BSocksClient socks_client; + int socks_up; +#endif + DebugObject d_obj; +} SocksUdpGwClient; + +#ifdef ANDROID_UDP +typedef struct { + BAddr local_addr; + BAddr remote_addr; +} SocksUdpGwClient_conaddr; + +typedef struct { + SocksUdpGwClient *client; + SocksUdpGwClient_conaddr conaddr; + BPending first_job; + const uint8_t *first_data; + int first_data_len; + BDatagram udp_dgram; + BufferWriter udp_send_writer; + PacketBuffer udp_send_buffer; + SinglePacketBuffer udp_recv_buffer; + PacketPassInterface udp_recv_if; + BAVLNode connections_tree_node; + LinkedList1Node connections_list_node; +} SocksUdpGwClient_connection; +#endif + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) WARN_UNUSED; +void SocksUdpGwClient_Free (SocksUdpGwClient *o); +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); + +#endif diff --git a/app/src/main/jni/badvpn/tun2socks/badvpn-tun2socks.8 b/app/src/main/jni/badvpn/tun2socks/badvpn-tun2socks.8 new file mode 100644 index 0000000..d1ab50c --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/badvpn-tun2socks.8 @@ -0,0 +1,126 @@ +.TH badvpn-tun2socks 8 "February 2012" +.SH NAME +badvpn-tun2socks \- create a TUN device to route TCP traffic through a SOCKS server +.SH SYNOPSIS +.PP +.B +badvpn-tun2socks +.br + [\fB\-\-help\fR] +.br + [\fB\-\-version\fR] +.br + [\fB\-\-logger\fR ] +.br + [\fB\-\-syslog-facility\fR ] [\fB\-\-syslog-ident\fR ] +.br + [\fB\-\-loglevel\fR <0-5/none/error/warning/notice/info/debug>] +.br + [\fB\-\-channel-loglevel\fR <0-5/none/error/warning/notice/info/debug>] ... +.br + [\fB\-\-tundev\fR ] +.br + \fB\-\-netif\-ipaddr\fR +.br + \fB\-\-netif\-netmask\fR +.br + \fB\-\-socks\-server\-addr\fR +.br + [\fB\-\-udpgw-remote-server-addr\fR ] +.br + [\fB\-\-udpgw-max-connections\fR ] +.br + [\fB\-\-udpgw-connection-buffer-size\fR ] +.PP +Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6). +.SH DESCRIPTION +.PP +badvpn-tun2socks +is a network utility used to "socksify" TCP connections at the network +layer. It implements a TUN device which accepts all incoming TCP +connections (regardless of destination IP), and forwards them through +a SOCKS server. This allows you to forward all connections through +SOCKS, without any need for application support. It can be used, for +example, to forward connections through a remote SSH server. +.SH EXAMPLE +.PP +This example demonstrates using tun2socks in combination with SSH's dynamic forwarding feature. + +Connect to the SSH server, passing -D localhost:1080 to the ssh +command to enable dynamic forwarding. This will make ssh open a local +SOCKS server which tun2socks forward connection through. + +First create a TUN device (eg. using openvpn): + +.nf + openvpn --mktun --dev tun0 --user +.fi + +Configure the IP of the new tun device: + +.nf + ifconfig tun0 10.0.0.1 netmask 255.255.255.0 +.fi + +Now start the badvpn-tun2socks program: + +.nf + badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.0.0.2 --netif-netmask 255.255.255.0 \\ + --socks-server-addr 127.0.0.1:1080 +.fi + +Note that the address 10.0.0.2 is not a typo. It specifies the IP address of the virtual +router inside the TUN device, and must be different from the IP of the +TUN interface itself (but in the same subnet). + +Now you should be able to ping the virtual router's IP (10.0.0.2): + +.nf + ping -n 10.0.0.2 +.fi + +All that remains is to route connections through the TUN device +instead of the existing default gateway. This is done as follows: + +1. Add a route to the SSH server through your existing gateway, with a +lower metric than the original default route. + +2. If your DNS servers are in a network that is not direcly attached (e.g. in the Internet), +also add routes for them (like for the SSH server). This is +needed because tun2socks does not forward UDP by default (see below). + +3. Add a default route through the virtual router in the TUN device, +with a lower metric than the original default route, but higher than +the SSH and DNS routes. + +This will make all external connections go through the TUN device, +except for the SSH connection (else SSH would go through the TUN +device, which would go through... SSH). + +For example (assuming there are no existing default routes with metric +<=6; otherwise remove them or change their metrics): + +.nf + route add gw metric 5 + + route add default gw 10.0.0.2 metric 6 +.fi +.SH UDP FORWARDING +tun2socks can forward UDP, however this requires a forwarder daemon, badvpn-udpgw to run +on the remote SSH server: + +.nf + badvpn-udpgw --listen-addr 127.0.0.1:7300 +.fi + +Then tell tun2socks to forward UDP via the forwarder: + +.nf + --udpgw-remote-server-addr 127.0.0.1:7300 +.fi +.SH COPYRIGHT +.PP +Copyright \(co 2010 Ambroz Bizjak +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/app/src/main/jni/badvpn/tun2socks/tun2socks.c b/app/src/main/jni/badvpn/tun2socks/tun2socks.c new file mode 100644 index 0000000..65d6792 --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/tun2socks.c @@ -0,0 +1,2379 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#ifdef ANDROID + +#include + +#include +#include +#include + +BAVL connections_tree; +typedef struct { + BAddr local_addr; + BAddr remote_addr; + uint16_t port; + int count; + BAVLNode connections_tree_node; +} Connection; + +static int conaddr_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + if (*v1 == *v2) return 0; + else if (*v1 > *v2) return 1; + else return -1; +} + +static Connection * find_connection (uint16_t port) +{ + BAVLNode *tree_node = BAVL_LookupExact(&connections_tree, &port); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, Connection, connections_tree_node); +} + +static void remove_connection (Connection *con) +{ + con->count -= 1; + if (con->count <= 0) + { + BAVL_Remove(&connections_tree, &con->connections_tree_node); + free(con); + } +} + +static void insert_connection (BAddr local_addr, BAddr remote_addr, uint16_t port) +{ + Connection * con = find_connection(port); + if (con != NULL) + con->count += 1; + else + { + Connection * tmp = (Connection *)malloc(sizeof(Connection)); + tmp->local_addr = local_addr; + tmp->remote_addr = remote_addr; + tmp->port = port; + tmp->count = 1; + BAVL_Insert(&connections_tree, &tmp->connections_tree_node, NULL); + } +} + +static void free_connections() +{ + while (!BAVL_IsEmpty(&connections_tree)) { + Connection *con = UPPER_OBJECT(BAVL_GetLast(&connections_tree), Connection, connections_tree_node); + BAVL_Remove(&connections_tree, &con->connections_tree_node); + } +} + +static void tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) + { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + +#endif + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +#define SYNC_DECL \ + BPending sync_mark; \ + +#define SYNC_FROMHERE \ + BPending_Init(&sync_mark, BReactor_PendingGroup(&ss), NULL, NULL); \ + BPending_Set(&sync_mark); + +#define SYNC_BREAK \ + BPending_Free(&sync_mark); + +#define SYNC_COMMIT \ + BReactor_Synchronize(&ss, &sync_mark.base); \ + BPending_Free(&sync_mark); + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *netif_ipaddr; + char *netif_netmask; + char *netif_ip6addr; + char *socks_server_addr; + char *username; + char *password; + char *password_file; + int append_source_to_username; + char *udpgw_remote_server_addr; + int udpgw_max_connections; + int udpgw_connection_buffer_size; + int udpgw_transparent_dns; +#ifdef ANDROID + int tun_fd; + int tun_mtu; + int fake_proc; + char *pid; + char *sock; + char *dnsgw; +#else + char *tundev; +#endif +} options; + +// TCP client +struct tcp_client { + dead_t dead; + dead_t dead_client; + LinkedList1Node list_node; + BAddr local_addr; + BAddr remote_addr; + struct tcp_pcb *pcb; + int client_closed; + uint8_t buf[TCP_WND]; + int buf_used; + char *socks_username; + BSocksClient socks_client; + int socks_up; + int socks_closed; + StreamPassInterface *socks_send_if; + StreamRecvInterface *socks_recv_if; + uint8_t socks_recv_buf[CLIENT_SOCKS_RECV_BUF_SIZE]; + int socks_recv_buf_used; + int socks_recv_buf_sent; + int socks_recv_waiting; + int socks_recv_tcp_pending; +}; + +// IP address of netif +BIPAddr netif_ipaddr; + +// netmask of netif +BIPAddr netif_netmask; + +// IP6 address of netif +struct ipv6_addr netif_ip6addr; + +// SOCKS server address +BAddr socks_server_addr; + +// allocated password file contents +uint8_t *password_file_contents; + +// SOCKS authentication information +struct BSocksClient_auth_info socks_auth_info[2]; +size_t socks_num_auth_info; + +// remote udpgw server addr, if provided +BAddr udpgw_remote_server_addr; + +// reactor +BReactor ss; + +// set to 1 by terminate +int quitting; + +// TUN device +BTap device; + +// device write buffer +uint8_t *device_write_buf; + +// device reading +SinglePacketBuffer device_read_buffer; +PacketPassInterface device_read_interface; + +// udpgw client +SocksUdpGwClient udpgw_client; +int udp_mtu; + +// TCP timer +BTimer tcp_timer; + +// job for initializing lwip +BPending lwip_init_job; + +// lwip netif +int have_netif; +struct netif netif; + +// lwip TCP listener +struct tcp_pcb *listener; + +// lwip TCP/IPv6 listener +struct tcp_pcb *listener_ip6; + +// TCP clients +LinkedList1 tcp_clients; + +// number of clients +int num_clients; + +#ifdef ANDROID +// Address of dnsgw +BAddr dnsgw; +void terminate (void); +#else +static void terminate (void); +#endif + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder); +static void lwip_init_job_hadler (void *unused); +static void tcp_timer_handler (void *unused); +static void device_error_handler (void *unused); +static void device_read_handler_send (void *unused, uint8_t *data, int data_len); +#ifdef ANDROID +static int process_device_dns_packet (uint8_t *data, int data_len); +#endif +static int process_device_udp_packet (uint8_t *data, int data_len); +static err_t netif_init_func (struct netif *netif); +static err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr); +static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +static err_t common_netif_output (struct netif *netif, struct pbuf *p); +static err_t netif_input_func (struct pbuf *p, struct netif *inp); +static void client_logfunc (struct tcp_client *client); +static void client_log (struct tcp_client *client, int level, const char *fmt, ...); +static err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err); +static void client_handle_freed_client (struct tcp_client *client); +static void client_free_client (struct tcp_client *client); +static void client_abort_client (struct tcp_client *client); +static void client_free_socks (struct tcp_client *client); +static void client_murder (struct tcp_client *client); +static void client_dealloc (struct tcp_client *client); +static void client_err_func (void *arg, err_t err); +static err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +static void client_socks_handler (struct tcp_client *client, int event); +static void client_send_to_socks (struct tcp_client *client); +static void client_socks_send_handler_done (struct tcp_client *client, int data_len); +static void client_socks_recv_initiate (struct tcp_client *client); +static void client_socks_recv_handler_done (struct tcp_client *client, int data_len); +static int client_socks_recv_send_out (struct tcp_client *client); +static err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len); +static void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +#ifdef ANDROID +static void daemonize(const char* path) { + + /* Our process ID and Session ID */ + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) { + FILE *file = fopen(path, "w"); + if (file == NULL) { + BLog(BLOG_ERROR, "Invalid pid file"); + exit(EXIT_FAILURE); + } + + fprintf(file, "%d", pid); + fclose(file); + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask(0); + + /* Open any logs here */ + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +} +#endif + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + if (options.fake_proc) { + // Fake process name to cheat on Lollipop + strcpy(argv[0], "net.typeblog.socks"); + prctl(PR_SET_NAME, "net.typeblog.socks"); + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + +#ifdef ANDROID + if (options.pid) { + daemonize(options.pid); + } +#endif + + // clear password contents pointer + password_file_contents = NULL; + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // set not quitting + quitting = 0; + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + +#ifdef ANDROID + // use supplied file descriptor + + int sock, fd; + struct sockaddr_un addr; + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + BLog(BLOG_ERROR, "socket() failed: %s (socket sock = %d)\n", strerror(errno), sock); + goto fail2; + } + + unlink(options.sock); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, options.sock, sizeof(addr.sun_path)-1); + + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + BLog(BLOG_ERROR, "bind() failed: %s (sock = %d)\n", strerror(errno), sock); + close(sock); + goto fail2; + } + + if (listen(sock, 5) == -1) { + BLog(BLOG_ERROR, "listen() failed: %s (sock = %d)\n", strerror(errno), sock); + close(sock); + goto fail2; + } + + for (;;) { + int sock2; + struct sockaddr_un remote; + int t = sizeof(remote); + if ((sock2 = accept(sock, (struct sockaddr *)&remote, &t)) == -1) { + BLog(BLOG_ERROR, "accept() failed: %s (sock = %d)\n", strerror(errno), sock); + continue; + } + if (ancil_recv_fd(sock2, &fd)) { + BLog(BLOG_ERROR, "ancil_recv_fd: %s (sock = %d)\n", strerror(errno), sock2); + close(sock2); + } else { + close(sock2); + BLog(BLOG_INFO, "received fd = %d", fd); + break; + } + } + close(sock); + + struct BTap_init_data init_data; + init_data.dev_type = BTAP_DEV_TUN; + init_data.init_type = BTAP_INIT_FD; + init_data.init.fd.fd = fd; + init_data.init.fd.mtu = options.tun_mtu; + + if (!BTap_Init2(&device, &ss, init_data, device_error_handler, NULL)) { + BLog(BLOG_ERROR, "BTap_Init2 failed"); + goto fail3; + } +#else + // init TUN device + if (!BTap_Init(&device, &ss, options.tundev, device_error_handler, NULL, 1)) { + BLog(BLOG_ERROR, "BTap_Init failed"); + goto fail3; + } +#endif + + // NOTE: the order of the following is important: + // first device writing must evaluate, + // then lwip (so it can send packets to the device), + // then device reading (so it can pass received packets to lwip). + + // init device reading + PacketPassInterface_Init(&device_read_interface, BTap_GetMTU(&device), device_read_handler_send, NULL, BReactor_PendingGroup(&ss)); + if (!SinglePacketBuffer_Init(&device_read_buffer, BTap_GetOutput(&device), &device_read_interface, BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + if (options.udpgw_remote_server_addr) { + // compute maximum UDP payload size we need to pass through udpgw + udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); + if (options.netif_ip6addr) { + int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); + if (udp_mtu < udp_ip6_mtu) { + udp_mtu = udp_ip6_mtu; + } + } + if (udp_mtu < 0) { + udp_mtu = 0; + } + + // make sure our UDP payloads aren't too large for udpgw + int udpgw_mtu = udpgw_compute_mtu(udp_mtu); + if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { + BLog(BLOG_ERROR, "device MTU is too large for UDP"); + goto fail4a; + } + + // init udpgw client + if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, + socks_server_addr, socks_auth_info, socks_num_auth_info, + udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received + )) { + BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed"); + goto fail4a; + } + } + + // init lwip init job + BPending_Init(&lwip_init_job, BReactor_PendingGroup(&ss), lwip_init_job_hadler, NULL); + BPending_Set(&lwip_init_job); + + // init device write buffer + if (!(device_write_buf = (uint8_t *)BAlloc(BTap_GetMTU(&device)))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto fail5; + } + + // init TCP timer + // it won't trigger before lwip is initialized, becuase the lwip init is a job + BTimer_Init(&tcp_timer, TCP_TMR_INTERVAL, tcp_timer_handler, NULL); + BReactor_SetTimer(&ss, &tcp_timer); + + // set no netif + have_netif = 0; + + // set no listener + listener = NULL; + listener_ip6 = NULL; + + // init clients list + LinkedList1_Init(&tcp_clients); + + // init number of clients + num_clients = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&tcp_clients)) { + struct tcp_client *client = UPPER_OBJECT(node, struct tcp_client, list_node); + client_murder(client); + } + + // free listener + if (listener_ip6) { + tcp_close(listener_ip6); + } + if (listener) { + tcp_close(listener); + } + + // free netif + if (have_netif) { + netif_remove(&netif); + } + +#ifdef ANDROID + BLog(BLOG_NOTICE, "Free TCP connections"); + tcp_remove(tcp_bound_pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + free_connections(); +#endif + + BReactor_RemoveTimer(&ss, &tcp_timer); + BFree(device_write_buf); + +fail5: + BPending_Free(&lwip_init_job); + if (options.udpgw_remote_server_addr) { + SocksUdpGwClient_Free(&udpgw_client); + } +fail4a: + SinglePacketBuffer_Free(&device_read_buffer); +fail4: + PacketPassInterface_Free(&device_read_interface); + BTap_Free(&device); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&ss); +fail1: + BFree(password_file_contents); + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void terminate (void) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "tearing down"); + + // set quitting + quitting = 1; + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" +#ifdef ANDROID + " [--fake-proc]\n" + " [--tunfd ]\n" + " [--tunmtu ]\n" + " [--dnsgw ]\n" + " [--pid ]\n" + " [--sock ]\n" +#else + " [--tundev ]\n" +#endif + " --netif-ipaddr \n" + " --netif-netmask \n" + " --socks-server-addr \n" + " [--netif-ip6addr ]\n" + " [--username ]\n" + " [--password ]\n" + " [--password-file ]\n" + " [--append-source-to-username]\n" +#ifdef ANDROID_UDP + " [--enable-udprelay]\n" + " [--udprelay-max-connections ]\n" +#else + " [--udpgw-remote-server-addr ]\n" + " [--udpgw-max-connections ]\n" + " [--udpgw-connection-buffer-size ]\n" + " [--udpgw-transparent-dns]\n" +#endif + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } +#ifdef ANDROID + options.tun_fd = -1; + options.tun_mtu = 1500; + options.fake_proc = 0; + options.pid = NULL; +#else + options.tundev = NULL; +#endif + options.netif_ipaddr = NULL; + options.netif_netmask = NULL; + options.netif_ip6addr = NULL; + options.socks_server_addr = NULL; + options.username = NULL; + options.password = NULL; + options.password_file = NULL; + options.append_source_to_username = 0; + options.udpgw_remote_server_addr = NULL; + options.udpgw_max_connections = DEFAULT_UDPGW_MAX_CONNECTIONS; + options.udpgw_connection_buffer_size = DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE; + options.udpgw_transparent_dns = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } +#ifdef ANDROID + else if (!strcmp(arg, "--fake-proc")) { + options.fake_proc = 1; + } + else if (!strcmp(arg, "--tunfd")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.tun_fd = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--tunmtu")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.tun_mtu = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--dnsgw")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.dnsgw = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--pid")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.pid = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--sock")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.sock = argv[i + 1]; + i++; + } +#else + else if (!strcmp(arg, "--tundev")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.tundev = argv[i + 1]; + i++; + } +#endif + else if (!strcmp(arg, "--netif-ipaddr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ipaddr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-netmask")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_netmask = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-ip6addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ip6addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--socks-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.socks_server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--username")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.username = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password-file")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password_file = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--append-source-to-username")) { + options.append_source_to_username = 1; + } +#ifdef ANDROID_UDP + else if (!strcmp(arg, "--enable-udprelay")) { + options.udpgw_remote_server_addr = "0.0.0.0:0"; +#else + else if (!strcmp(arg, "--udpgw-remote-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.udpgw_remote_server_addr = argv[i + 1]; + i++; +#endif + } +#ifdef ANDROID_UDP + else if (!strcmp(arg, "--udprelay-max-connections")) { +#else + else if (!strcmp(arg, "--udpgw-max-connections")) { +#endif + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_max_connections = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } +#ifndef ANDROID_UDP + else if (!strcmp(arg, "--udpgw-connection-buffer-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_connection_buffer_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--udpgw-transparent-dns")) { + options.udpgw_transparent_dns = 1; + } +#endif + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.netif_ipaddr) { + fprintf(stderr, "--netif-ipaddr is required\n"); + return 0; + } + + if (!options.netif_netmask) { + fprintf(stderr, "--netif-netmask is required\n"); + return 0; + } + + if (!options.socks_server_addr) { + fprintf(stderr, "--socks-server-addr is required\n"); + return 0; + } + + if (options.username) { + if (!options.password && !options.password_file) { + fprintf(stderr, "username given but password not given\n"); + return 0; + } + + if (options.password && options.password_file) { + fprintf(stderr, "--password and --password-file cannot both be given\n"); + return 0; + } + } + +#ifdef ANDROID + if (!options.sock) { + fprintf(stderr, "--sock is required\n"); + return 0; + } +#endif + + return 1; +} + +int process_arguments (void) +{ + ASSERT(!password_file_contents) + + // resolve netif ipaddr + if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) { + BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed"); + return 0; + } + if (netif_ipaddr.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif ipaddr: must be an IPv4 address"); + return 0; + } + + // resolve netif netmask + if (!BIPAddr_Resolve(&netif_netmask, options.netif_netmask, 0)) { + BLog(BLOG_ERROR, "netif netmask: BIPAddr_Resolve failed"); + return 0; + } + if (netif_netmask.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif netmask: must be an IPv4 address"); + return 0; + } + + // parse IP6 address + if (options.netif_ip6addr) { + if (!ipaddr6_parse_ipv6_addr(options.netif_ip6addr, &netif_ip6addr)) { + BLog(BLOG_ERROR, "netif ip6addr: incorrect"); + return 0; + } + } + + // resolve SOCKS server address + if (!BAddr_Parse2(&socks_server_addr, options.socks_server_addr, NULL, 0, 0)) { + BLog(BLOG_ERROR, "socks server addr: BAddr_Parse2 failed"); + return 0; + } + + // add none socks authentication method + socks_auth_info[0] = BSocksClient_auth_none(); + socks_num_auth_info = 1; + + // add password socks authentication method + if (options.username) { + const char *password; + size_t password_len; + if (options.password) { + password = options.password; + password_len = strlen(options.password); + } else { + if (!read_file(options.password_file, &password_file_contents, &password_len)) { + BLog(BLOG_ERROR, "failed to read password file"); + return 0; + } + password = (char *)password_file_contents; + } + + socks_auth_info[socks_num_auth_info++] = BSocksClient_auth_password( + options.username, strlen(options.username), + password, password_len + ); + } + + // resolve remote udpgw server address + if (options.udpgw_remote_server_addr) { + if (!BAddr_Parse2(&udpgw_remote_server_addr, options.udpgw_remote_server_addr, NULL, 0, 0)) { +#ifdef ANDROID_UDP + BLog(BLOG_ERROR, "udprelay server addr: BAddr_Parse2 failed"); +#else + BLog(BLOG_ERROR, "remote udpgw server addr: BAddr_Parse2 failed"); +#endif + return 0; + } + } + +#ifdef ANDROID + // resolve dnsgw addr + if (options.dnsgw) { + if (!BAddr_Parse2(&dnsgw, options.dnsgw, NULL, 0, 0)) { + BLog(BLOG_ERROR, "dnsgw addr: BAddr_Parse2 failed"); + return 0; + } + if (dnsgw.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "dnsgw addr: must be an IPv4 address"); + return 0; + } + } +#endif + + return 1; +} + +void signal_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder) +{ + BAddr addr; + if (is_ipv6) { + BAddr_InitIPv6(&addr, (uint8_t *)ipx_addr->ip6.addr, hton16(port_hostorder)); + } else { + BAddr_InitIPv4(&addr, ipx_addr->ip4.addr, hton16(port_hostorder)); + } + return addr; +} + +void lwip_init_job_hadler (void *unused) +{ + ASSERT(!quitting) + ASSERT(netif_ipaddr.type == BADDR_TYPE_IPV4) + ASSERT(netif_netmask.type == BADDR_TYPE_IPV4) + ASSERT(!have_netif) + ASSERT(!listener) + ASSERT(!listener_ip6) + + BLog(BLOG_DEBUG, "lwip init"); + + // NOTE: the device may fail during this, but there's no harm in not checking + // for that at every step + + // init lwip + lwip_init(); + + // make addresses for netif + ip_addr_t addr; + addr.addr = netif_ipaddr.ipv4; + ip_addr_t netmask; + netmask.addr = netif_netmask.ipv4; + ip_addr_t gw; + ip_addr_set_any(&gw); + + // init netif + if (!netif_add(&netif, &addr, &netmask, &gw, NULL, netif_init_func, netif_input_func)) { + BLog(BLOG_ERROR, "netif_add failed"); + goto fail; + } + have_netif = 1; + + // set netif up + netif_set_up(&netif); + + // set netif pretend TCP + netif_set_pretend_tcp(&netif, 1); + + // set netif default + netif_set_default(&netif); + + if (options.netif_ip6addr) { + // add IPv6 address + memcpy(netif_ip6_addr(&netif, 0), netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes)); + netif_ip6_addr_set_state(&netif, 0, IP6_ADDR_VALID); + } + + // init listener + struct tcp_pcb *l = tcp_new(); + if (!l) { + BLog(BLOG_ERROR, "tcp_new failed"); + goto fail; + } + + // bind listener + if (tcp_bind_to_netif(l, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l); + goto fail; + } + + // listen listener + if (!(listener = tcp_listen(l))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l); + goto fail; + } + + // setup listener accept handler + tcp_accept(listener, listener_accept_func); + + if (options.netif_ip6addr) { + struct tcp_pcb *l_ip6 = tcp_new_ip6(); + if (!l_ip6) { + BLog(BLOG_ERROR, "tcp_new_ip6 failed"); + goto fail; + } + + if (tcp_bind_to_netif(l_ip6, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l_ip6); + goto fail; + } + + if (!(listener_ip6 = tcp_listen(l_ip6))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l_ip6); + goto fail; + } + + tcp_accept(listener_ip6, listener_accept_func); + } + + return; + +fail: + if (!quitting) { + terminate(); + } +} + +void tcp_timer_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_DEBUG, "TCP timer"); + + // schedule next timer + // TODO: calculate timeout so we don't drift + BReactor_SetTimer(&ss, &tcp_timer); + + tcp_tmr(); + return; +} + +void device_error_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_ERROR, "device error"); + + terminate(); + return; +} + +void device_read_handler_send (void *unused, uint8_t *data, int data_len) +{ + ASSERT(!quitting) + ASSERT(data_len >= 0) + + BLog(BLOG_DEBUG, "device: received packet"); + + // accept packet + PacketPassInterface_Done(&device_read_interface); + +#ifdef ANDROID + // process DNS directly + if (process_device_dns_packet(data, data_len)) { + return; + } +#endif + + // process UDP directly + if (process_device_udp_packet(data, data_len)) { + return; + } + + // obtain pbuf + if (data_len > UINT16_MAX) { + BLog(BLOG_WARNING, "device read: packet too large"); + return; + } + struct pbuf *p = pbuf_alloc(PBUF_RAW, data_len, PBUF_POOL); + if (!p) { + BLog(BLOG_WARNING, "device read: pbuf_alloc failed"); + return; + } + + // write packet to pbuf + ASSERT_FORCE(pbuf_take(p, data, data_len) == ERR_OK) + + // pass pbuf to input + if (netif.input(p, &netif) != ERR_OK) { + BLog(BLOG_WARNING, "device read: input failed"); + pbuf_free(p); + } +} + +#ifdef ANDROID +int process_device_dns_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have dnsgw + if (!options.dnsgw) { + goto fail; + } + + static int init = 0; + + int to_dns; + int from_dns; + int packet_length = 0; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + // to port 53 is considered a DNS packet + to_dns = udp_header.dest_port == hton16(53); + + // from port 8153 is considered a DNS packet + from_dns = udp_header.source_port == dnsgw.ipv4.port; + + // if not DNS packet, just bypass it. + if (!to_dns && !from_dns) { + goto fail; + } + + // modify DNS packet + if (to_dns) { + BLog(BLOG_INFO, "UDP: to DNS %d bytes", data_len); + + // construct addresses + if (!init) { + init = 1; + BAVL_Init(&connections_tree, OFFSET_DIFF(Connection, port, connections_tree_node), (BAVL_comparator)conaddr_comparator, NULL); + } + BAddr local_addr; + BAddr remote_addr; + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + insert_connection(local_addr, remote_addr, udp_header.source_port); + + // build IP header + ipv4_header.destination_address = dnsgw.ipv4.ip; + ipv4_header.source_address = netif_ipaddr.ipv4; + + // build UDP header + udp_header.dest_port = dnsgw.ipv4.port; + + } else if (from_dns) { + + // if not initialized + if (!init) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from DNS %d bytes", data_len); + + Connection * con = find_connection(udp_header.dest_port); + if (con != NULL) + { + // build IP header + ipv4_header.source_address = con->remote_addr.ipv4.ip; + ipv4_header.destination_address = con->local_addr.ipv4.ip; + + // build UDP header + udp_header.source_port = con->remote_addr.ipv4.port; + + remove_connection(con); + + } + else + { + goto fail; + } + } + + // update IPv4 header's checksum + ipv4_header.checksum = hton16(0); + ipv4_header.checksum = ipv4_checksum(&ipv4_header, NULL, 0); + + // update UDP header's checksum + udp_header.checksum = hton16(0); + udp_header.checksum = udp_checksum(&udp_header, data, data_len, + ipv4_header.source_address, ipv4_header.destination_address); + + // write packet + memcpy(device_write_buf, &ipv4_header, sizeof(ipv4_header)); + memcpy(device_write_buf + sizeof(ipv4_header), &udp_header, sizeof(udp_header)); + memcpy(device_write_buf + sizeof(ipv4_header) + sizeof(udp_header), data, data_len); + packet_length = sizeof(ipv4_header) + sizeof(udp_header) + data_len; + + } break; + + case 6: { + // TODO: support IPv6 DNS Gateway + goto fail; + } break; + + default: { + goto fail; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); + + return 1; + +fail: + return 0; +} +#endif + +int process_device_udp_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have udpgw + if (!options.udpgw_remote_server_addr) { + goto fail; + } + + BAddr local_addr; + BAddr remote_addr; + int is_dns; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + + // if transparent DNS is enabled, any packet arriving at out netif + // address to port 53 is considered a DNS packet + is_dns = (options.udpgw_transparent_dns && + ipv4_header.destination_address == netif_ipaddr.ipv4 && + udp_header.dest_port == hton16(53)); + } break; + + case 6: { + // ignore if IPv6 support is disabled + if (!options.netif_ip6addr) { + goto fail; + } + + // ignore non-UDP packets + if (data_len < sizeof(struct ipv6_header) || data[offsetof(struct ipv6_header, next_header)] != IPV6_NEXT_UDP) { + goto fail; + } + + // parse IPv6 header + struct ipv6_header ipv6_header; + if (!ipv6_check(data, data_len, &ipv6_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_ip6_checksum(&udp_header, data, data_len, ipv6_header.source_address, ipv6_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP/IPv6: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv6(&local_addr, ipv6_header.source_address, udp_header.source_port); + BAddr_InitIPv6(&remote_addr, ipv6_header.destination_address, udp_header.dest_port); + + // TODO dns + is_dns = 0; + } break; + + default: { + goto fail; + } break; + } + + // check payload length + if (data_len > udp_mtu) { + BLog(BLOG_ERROR, "packet is too large, cannot send to udpgw"); + goto fail; + } + + // submit packet to udpgw + SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, is_dns, data, data_len); + + return 1; + +fail: + return 0; +} + +err_t netif_init_func (struct netif *netif) +{ + BLog(BLOG_DEBUG, "netif func init"); + + netif->name[0] = 'h'; + netif->name[1] = 'o'; + netif->output = netif_output_func; + netif->output_ip6 = netif_output_ip6_func; + + return ERR_OK; +} + +err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t common_netif_output (struct netif *netif, struct pbuf *p) +{ + SYNC_DECL + + BLog(BLOG_DEBUG, "device write: send packet"); + + if (quitting) { + return ERR_OK; + } + + // if there is just one chunk, send it directly, else via buffer + if (!p->next) { + if (p->len > BTap_GetMTU(&device)) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + + SYNC_FROMHERE + BTap_Send(&device, (uint8_t *)p->payload, p->len); + SYNC_COMMIT + } else { + int len = 0; + do { + if (p->len > BTap_GetMTU(&device) - len) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + memcpy(device_write_buf + len, p->payload, p->len); + len += p->len; + } while (p = p->next); + + SYNC_FROMHERE + BTap_Send(&device, device_write_buf, len); + SYNC_COMMIT + } + +out: + return ERR_OK; +} + +err_t netif_input_func (struct pbuf *p, struct netif *inp) +{ + uint8_t ip_version = 0; + if (p->len > 0) { + ip_version = (((uint8_t *)p->payload)[0] >> 4); + } + + switch (ip_version) { + case 4: { + return ip_input(p, inp); + } break; + case 6: { + if (options.netif_ip6addr) { + return ip6_input(p, inp); + } + } break; + } + + pbuf_free(p); + return ERR_OK; +} + +void client_logfunc (struct tcp_client *client) +{ + char local_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->local_addr, local_addr_s); + char remote_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, remote_addr_s); + + BLog_Append("%05d (%s %s): ", num_clients, local_addr_s, remote_addr_s); +} + +void client_log (struct tcp_client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err) +{ + ASSERT(err == ERR_OK) + + // signal accepted + struct tcp_pcb *this_listener = (PCB_ISIPV6(newpcb) ? listener_ip6 : listener); + tcp_accepted(this_listener); + + // allocate client structure + struct tcp_client *client = (struct tcp_client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "listener accept: malloc failed"); + goto fail0; + } + client->socks_username = NULL; + + SYNC_DECL + SYNC_FROMHERE + + // read addresses + client->local_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->local_ip, newpcb->local_port); + client->remote_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->remote_ip, newpcb->remote_port); + + // get destination address + BAddr addr = client->local_addr; +#ifdef OVERRIDE_DEST_ADDR + ASSERT_FORCE(BAddr_Parse2(&addr, OVERRIDE_DEST_ADDR, NULL, 0, 1)) +#endif + + // add source address to username if requested + if (options.username && options.append_source_to_username) { + char addr_str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, addr_str); + client->socks_username = concat_strings(3, options.username, "@", addr_str); + if (!client->socks_username) { + goto fail1; + } + socks_auth_info[1].password.username = client->socks_username; + socks_auth_info[1].password.username_len = strlen(client->socks_username); + } + + // init SOCKS + if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info, + addr, (BSocksClient_handler)client_socks_handler, client, &ss)) { + BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed"); + goto fail1; + } + + // init dead vars + DEAD_INIT(client->dead); + DEAD_INIT(client->dead_client); + + // add to linked list + LinkedList1_Append(&tcp_clients, &client->list_node); + + // increment counter + ASSERT(num_clients >= 0) + num_clients++; + + // set pcb + client->pcb = newpcb; + + // set client not closed + client->client_closed = 0; + + // enable TCP_NODELAY + tcp_nagle_disable(client->pcb); + + // setup handler argument + tcp_arg(client->pcb, client); + + // setup handlers + tcp_err(client->pcb, client_err_func); + tcp_recv(client->pcb, client_recv_func); + + // setup buffer + client->buf_used = 0; + + // set SOCKS not up, not closed + client->socks_up = 0; + client->socks_closed = 0; + + client_log(client, BLOG_INFO, "accepted"); + + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + + return ERR_OK; + +fail1: + SYNC_BREAK + free(client->socks_username); + free(client); +fail0: + return ERR_MEM; +} + +void client_handle_freed_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // pcb was taken care of by the caller + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + + // if we have data to be sent to SOCKS and can send it, keep sending + if (client->buf_used > 0 && !client->socks_closed) { + client_log(client, BLOG_INFO, "waiting untill buffered data is sent to SOCKS"); + } else { + if (!client->socks_closed) { + client_free_socks(client); + } else { + client_dealloc(client); + } + } +} + +void client_free_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + err_t err = tcp_close(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_ERROR, "tcp_close failed (%d)", err); + tcp_abort(client->pcb); + } + + client_handle_freed_client(client); +} + +void client_abort_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + tcp_abort(client->pcb); + + client_handle_freed_client(client); +} + +void client_free_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + + // stop sending to SOCKS + if (client->socks_up) { + // stop receiving from client + if (!client->client_closed) { + tcp_recv(client->pcb, NULL); + } + } + + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + + // if we have data to be sent to the client and we can send it, keep sending + if (client->socks_up && (client->socks_recv_buf_used >= 0 || client->socks_recv_tcp_pending > 0) && !client->client_closed) { + client_log(client, BLOG_INFO, "waiting until buffered data is sent to client"); + } else { + if (!client->client_closed) { + client_free_client(client); + } else { + client_dealloc(client); + } + } +} + +void client_murder (struct tcp_client *client) +{ + // free client + if (!client->client_closed) { + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // abort + tcp_abort(client->pcb); + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + } + + // free SOCKS + if (!client->socks_closed) { + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + } + + // dealloc entry + client_dealloc(client); +} + +void client_dealloc (struct tcp_client *client) +{ + ASSERT(client->client_closed) + ASSERT(client->socks_closed) + + // decrement counter + ASSERT(num_clients > 0) + num_clients--; + + // remove client entry + LinkedList1_Remove(&tcp_clients, &client->list_node); + + // kill dead var + DEAD_KILL(client->dead); + + // free memory + free(client->socks_username); + free(client); +} + +void client_err_func (void *arg, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + + client_log(client, BLOG_INFO, "client error (%d)", (int)err); + + // the pcb was taken care of by the caller + client_handle_freed_client(client); +} + +err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + ASSERT(err == ERR_OK) // checked in lwIP source. Otherwise, I've no idea what should + // be done with the pbuf in case of an error. + + if (!p) { + client_log(client, BLOG_INFO, "client closed"); + client_free_client(client); + return ERR_ABRT; + } + + ASSERT(p->tot_len > 0) + + // check if we have enough buffer + if (p->tot_len > sizeof(client->buf) - client->buf_used) { + client_log(client, BLOG_ERROR, "no buffer for data !?!"); + return ERR_MEM; + } + + // copy data to buffer + ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len) + client->buf_used += p->tot_len; + + // if there was nothing in the buffer before, and SOCKS is up, start send data + if (client->buf_used == p->tot_len && client->socks_up) { + ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed + + SYNC_DECL + SYNC_FROMHERE + client_send_to_socks(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + // free pbuff + pbuf_free(p); + + return ERR_OK; +} + +void client_socks_handler (struct tcp_client *client, int event) +{ + ASSERT(!client->socks_closed) + + switch (event) { + case BSOCKSCLIENT_EVENT_ERROR: { + client_log(client, BLOG_INFO, "SOCKS error"); + + client_free_socks(client); + } break; + + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS up"); + + // init sending + client->socks_send_if = BSocksClient_GetSendInterface(&client->socks_client); + StreamPassInterface_Sender_Init(client->socks_send_if, (StreamPassInterface_handler_done)client_socks_send_handler_done, client); + + // init receiving + client->socks_recv_if = BSocksClient_GetRecvInterface(&client->socks_client); + StreamRecvInterface_Receiver_Init(client->socks_recv_if, (StreamRecvInterface_handler_done)client_socks_recv_handler_done, client); + client->socks_recv_buf_used = -1; + client->socks_recv_tcp_pending = 0; + if (!client->client_closed) { + tcp_sent(client->pcb, client_sent_func); + } + + // set up + client->socks_up = 1; + + // start sending data if there is any + if (client->buf_used > 0) { + client_send_to_socks(client); + } + + // start receiving data if client is still up + if (!client->client_closed) { + client_socks_recv_initiate(client); + } + } break; + + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + ASSERT(client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS closed"); + + client_free_socks(client); + } break; + + default: + ASSERT(0); + } +} + +void client_send_to_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + + // schedule sending + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); +} + +void client_socks_send_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + ASSERT(data_len > 0) + ASSERT(data_len <= client->buf_used) + + // remove sent data from buffer + memmove(client->buf, client->buf + data_len, client->buf_used - data_len); + client->buf_used -= data_len; + + if (!client->client_closed) { + // confirm sent data + tcp_recved(client->pcb, data_len); + } + + if (client->buf_used > 0) { + // send any further data + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); + } + else if (client->client_closed) { + // client was closed we've sent everything we had buffered; we're done with it + client_log(client, BLOG_INFO, "removing after client went down"); + + client_free_socks(client); + } +} + +void client_socks_recv_initiate (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + StreamRecvInterface_Receiver_Recv(client->socks_recv_if, client->socks_recv_buf, sizeof(client->socks_recv_buf)); +} + +void client_socks_recv_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= sizeof(client->socks_recv_buf)) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + // if client was closed, stop receiving + if (client->client_closed) { + return; + } + + // set amount of data in buffer + client->socks_recv_buf_used = data_len; + client->socks_recv_buf_sent = 0; + client->socks_recv_waiting = 0; + + // send to client + if (client_socks_recv_send_out(client) < 0) { + return; + } + + // continue receiving if needed + if (client->socks_recv_buf_used == -1) { + client_socks_recv_initiate(client); + } +} + +int client_socks_recv_send_out (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used > 0) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + ASSERT(!client->socks_recv_waiting) + + // return value -1 means tcp_abort() was done, + // 0 means it wasn't and the client (pcb) is still up + + do { + int to_write = bmin_int(client->socks_recv_buf_used - client->socks_recv_buf_sent, tcp_sndbuf(client->pcb)); + if (to_write == 0) { + break; + } + + err_t err = tcp_write(client->pcb, client->socks_recv_buf + client->socks_recv_buf_sent, to_write, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + if (err == ERR_MEM) { + break; + } + + client_log(client, BLOG_INFO, "tcp_write failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + client->socks_recv_buf_sent += to_write; + client->socks_recv_tcp_pending += to_write; + } while (client->socks_recv_buf_sent < client->socks_recv_buf_used); + + // start sending now + err_t err = tcp_output(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_INFO, "tcp_output failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + // more data to queue? + if (client->socks_recv_buf_sent < client->socks_recv_buf_used) { + if (client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_ERROR, "can't queue data, but all data was confirmed !?!"); + + client_abort_client(client); + return -1; + } + + // set waiting, continue in client_sent_func + client->socks_recv_waiting = 1; + return 0; + } + + // everything was queued + client->socks_recv_buf_used = -1; + + return 0; +} + +err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + struct tcp_client *client = (struct tcp_client *)arg; + + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(len > 0) + ASSERT(len <= client->socks_recv_tcp_pending) + + // decrement pending + client->socks_recv_tcp_pending -= len; + + // continue queuing + if (client->socks_recv_buf_used > 0) { + ASSERT(client->socks_recv_waiting) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + + // set not waiting + client->socks_recv_waiting = 0; + + // possibly send more data + if (client_socks_recv_send_out(client) < 0) { + return ERR_ABRT; + } + + // we just queued some data, so it can't have been confirmed yet + ASSERT(client->socks_recv_tcp_pending > 0) + + // continue receiving if needed + if (client->socks_recv_buf_used == -1 && !client->socks_closed) { + SYNC_DECL + SYNC_FROMHERE + client_socks_recv_initiate(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + return ERR_OK; + } + + // have we sent everything after SOCKS was closed? + if (client->socks_closed && client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_INFO, "removing after SOCKS went down"); + client_free_client(client); + return ERR_ABRT; + } + + return ERR_OK; +} + +void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + ASSERT(options.udpgw_remote_server_addr) + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(local_addr.type == remote_addr.type) + ASSERT(data_len >= 0) + + int packet_length = 0; + + switch (local_addr.type) { + case BADDR_TYPE_IPV4: { +#ifdef ANDROID + BLog(BLOG_INFO, "UDP: from udprelay %d bytes", data_len); +#else + BLog(BLOG_INFO, "UDP: from udpgw %d bytes", data_len); +#endif + + if (data_len > UINT16_MAX - (sizeof(struct ipv4_header) + sizeof(struct udp_header)) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP: packet is too large"); + return; + } + + // build IP header + struct ipv4_header iph; + iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph)); + iph.ds = hton8(0); + iph.total_length = hton16(sizeof(iph) + sizeof(struct udp_header) + data_len); + iph.identification = hton16(0); + iph.flags3_fragmentoffset13 = hton16(0); + iph.ttl = hton8(64); + iph.protocol = hton8(IPV4_PROTOCOL_UDP); + iph.checksum = hton16(0); + iph.source_address = remote_addr.ipv4.ip; + iph.destination_address = local_addr.ipv4.ip; + iph.checksum = ipv4_checksum(&iph, NULL, 0); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv4.port; + udph.dest_port = local_addr.ipv4.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + + case BADDR_TYPE_IPV6: { +#ifdef ANDROID_UDP + BLog(BLOG_INFO, "UDP/IPv6: from udprelay %d bytes", data_len); +#else + BLog(BLOG_INFO, "UDP/IPv6: from udpgw %d bytes", data_len); +#endif + + if (!options.netif_ip6addr) { +#ifdef ANDROID_UDP + BLog(BLOG_ERROR, "got IPv6 packet from udprelay but IPv6 is disabled"); +#else + BLog(BLOG_ERROR, "got IPv6 packet from udpgw but IPv6 is disabled"); +#endif + return; + } + + if (data_len > UINT16_MAX - sizeof(struct udp_header) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP/IPv6: packet is too large"); + return; + } + + // build IPv6 header + struct ipv6_header iph; + iph.version4_tc4 = hton8((6 << 4)); + iph.tc4_fl4 = hton8(0); + iph.fl = hton16(0); + iph.payload_length = hton16(sizeof(struct udp_header) + data_len); + iph.next_header = hton8(IPV6_NEXT_UDP); + iph.hop_limit = hton8(64); + memcpy(iph.source_address, remote_addr.ipv6.ip, 16); + memcpy(iph.destination_address, local_addr.ipv6.ip, 16); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv6.port; + udph.dest_port = local_addr.ipv6.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_ip6_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); +} diff --git a/app/src/main/jni/badvpn/tun2socks/tun2socks.h b/app/src/main/jni/badvpn/tun2socks/tun2socks.h new file mode 100644 index 0000000..caf5778 --- /dev/null +++ b/app/src/main/jni/badvpn/tun2socks/tun2socks.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Ambroz Bizjak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "tun2socks" + +// size of temporary buffer for passing data from the SOCKS server to TCP for sending +#define CLIENT_SOCKS_RECV_BUF_SIZE 8192 + +// maximum number of udpgw connections +#define DEFAULT_UDPGW_MAX_CONNECTIONS 256 + +// udpgw per-connection send buffer size, in number of packets +#define DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE 8 + +// udpgw reconnect time after connection fails +#define UDPGW_RECONNECT_TIME 5000 + +// udpgw keepalive sending interval +#define UDPGW_KEEPALIVE_TIME 10000 + +// option to override the destination addresses to give the SOCKS server +//#define OVERRIDE_DEST_ADDR "10.111.0.2:2000" diff --git a/app/src/main/jni/badvpn/tunctl/CMakeLists.txt b/app/src/main/jni/badvpn/tunctl/CMakeLists.txt new file mode 100644 index 0000000..4cbebc8 --- /dev/null +++ b/app/src/main/jni/badvpn/tunctl/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(badvpn-tunctl tunctl.c) + +install( + TARGETS badvpn-tunctl + RUNTIME DESTINATION bin +) diff --git a/app/src/main/jni/badvpn/tunctl/tunctl.c b/app/src/main/jni/badvpn/tunctl/tunctl.c new file mode 100644 index 0000000..4490adc --- /dev/null +++ b/app/src/main/jni/badvpn/tunctl/tunctl.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM_NAME "tunctl" + +#define TUN_DEVNODE "/dev/net/tun" + +struct { + int help; + int version; + int op; + char *device_name; + char *user; + char *group; +} options; + +#define OP_MKTUN 1 +#define OP_MKTAP 2 +#define OP_RMTUN 3 +#define OP_RMTAP 4 + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group); +static int remove_tuntap (const char *ifname, int is_tun); + +int main (int argc, char *argv[]) +{ + int res = 1; + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Error: Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + if (options.op == OP_MKTUN || options.op == OP_MKTAP) { + if (!options.user && !options.group) { + fprintf(stderr, "WARNING: with neither --user nor --group, anyone will be able to use the device!\n"); + } + res = !make_tuntap(options.device_name, options.op == OP_MKTUN, options.user, options.group); + } else { + res = !remove_tuntap(options.device_name, options.op == OP_RMTUN); + } + +fail0: + return res; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s [--help] [--version]\n" + " %s --mktun [--user ] [--group ]\n" + " %s --mktap [--user ] [--group ]\n" + " %s --rmtun \n" + " %s --rmtap \n", + name, name, name, name, name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.op = -1; + options.device_name = NULL; + options.user = NULL; + options.group = NULL; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--mktun")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_MKTUN; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--mktap")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_MKTAP; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--rmtun")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_RMTUN; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--rmtap")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_RMTAP; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--user")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.user = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--group")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.group = argv[i + 1]; + i++; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.op < 0) { + fprintf(stderr, "--mktun, --mktap --rmtun or --rmtap is required\n"); + return 0; + } + + if ((options.user || options.group) && options.op != OP_MKTUN && options.op != OP_MKTAP) { + fprintf(stderr, "--user and --group only make sense for --mktun and --mktap\n"); + return 0; + } + + return 1; +} + +static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group) +{ + int res = 0; + + if (strlen(ifname) >= IFNAMSIZ) { + fprintf(stderr, "Error: ifname too long\n"); + goto fail0; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + perror("open"); + fprintf(stderr, "Error: open tun failed\n"); + goto fail0; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + perror("ioctl(TUNSETIFF)"); + fprintf(stderr, "Error: TUNSETIFF failed\n"); + goto fail1; + } + + uid_t uid = -1; + gid_t gid = -1; + + if (user) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + fprintf(stderr, "Error: malloc failed\n"); + goto fail1; + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(user, &pwd, buf, bufsize, &res); + if (!res) { + fprintf(stderr, "Error: getpwnam_r failed\n"); + free(buf); + goto fail1; + } + + uid = pwd.pw_uid; + free(buf); + } + + if (group) { + long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + fprintf(stderr, "Error: malloc failed\n"); + goto fail1; + } + + struct group grp; + struct group *res; + getgrnam_r(group, &grp, buf, bufsize, &res); + if (!res) { + fprintf(stderr, "Error: getgrnam_r failed\n"); + free(buf); + goto fail1; + } + + gid = grp.gr_gid; + free(buf); + } + + if (ioctl(fd, TUNSETOWNER, uid) < 0) { + perror("ioctl(TUNSETOWNER)"); + fprintf(stderr, "Error: TUNSETOWNER failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETGROUP, gid) < 0) { + perror("ioctl(TUNSETGROUP)"); + fprintf(stderr, "Error: TUNSETGROUP failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) { + perror("ioctl(TUNSETPERSIST)"); + fprintf(stderr, "Error: TUNSETPERSIST failed\n"); + goto fail1; + } + + res = 1; + +fail1: + close(fd); +fail0: + return res; +} + +static int remove_tuntap (const char *ifname, int is_tun) +{ + int res = 0; + + if (strlen(ifname) >= IFNAMSIZ) { + fprintf(stderr, "Error: ifname too long\n"); + goto fail0; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + perror("open"); + fprintf(stderr, "Error: open tun failed\n"); + goto fail0; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + perror("ioctl(TUNSETIFF)"); + fprintf(stderr, "Error: TUNSETIFF failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) { + perror("ioctl(TUNSETPERSIST)"); + fprintf(stderr, "Error: TUNSETPERSIST failed\n"); + goto fail1; + } + + res = 1; + +fail1: + close(fd); +fail0: + return res; +} diff --git a/app/src/main/jni/badvpn/tuntap/BTap.c b/app/src/main/jni/badvpn/tuntap/BTap.c new file mode 100644 index 0000000..43d3e7a --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/BTap.c @@ -0,0 +1,577 @@ +/** + * @file BTap.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef BADVPN_USE_WINAPI + #include + #include + #include + #include + #include "wintap-common.h" + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #ifdef BADVPN_LINUX + #include + #endif + #ifdef BADVPN_FREEBSD + #include + #include + #endif +#endif + +#include + +#include + +#include + +static void report_error (BTap *o); +static void output_handler_recv (BTap *o, uint8_t *data); + +#ifdef BADVPN_USE_WINAPI + +static void recv_olap_handler (BTap *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_packet) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set no output packet + o->output_packet = NULL; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "read operation failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->frame_mtu) + + // done + PacketRecvInterface_Done(&o->output, bytes); +} + +#else + +static void fd_handler (BTap *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + if (events&(BREACTOR_ERROR|BREACTOR_HUP)) { + BLog(BLOG_WARNING, "device fd reports error?"); + } + + if (events&BREACTOR_READ) do { + ASSERT(o->output_packet) + + // try reading into the buffer + int bytes = read(o->fd, o->output_packet, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later + break; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + // set no output packet + o->output_packet = NULL; + + // update events + o->poll_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + + // inform receiver we finished the packet + PacketRecvInterface_Done(&o->output, bytes); + } while (0); +} + +#endif + +void report_error (BTap *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->handler_error_user)); +} + +void output_handler_recv (BTap *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data) + ASSERT(!o->output_packet) + +#ifdef BADVPN_USE_WINAPI + + memset(&o->recv_olap.olap, 0, sizeof(o->recv_olap.olap)); + + // read + BOOL res = ReadFile(o->device, data, o->frame_mtu, NULL, &o->recv_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "ReadFile failed (%u)", GetLastError()); + report_error(o); + return; + } + + o->output_packet = data; + +#else + + // attempt read + int bytes = read(o->fd, data, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later in fd_handler + // remember packet + o->output_packet = data; + // update events + o->poll_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + return; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + PacketRecvInterface_Done(&o->output, bytes); + +#endif +} + +int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) +{ + ASSERT(tun == 0 || tun == 1) + + struct BTap_init_data init_data; + init_data.dev_type = tun ? BTAP_DEV_TUN : BTAP_DEV_TAP; + init_data.init_type = BTAP_INIT_STRING; + init_data.init.string = devname; + + return BTap_Init2(o, reactor, init_data, handler_error, handler_error_user); +} + +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) +{ + ASSERT(init_data.dev_type == BTAP_DEV_TUN || init_data.dev_type == BTAP_DEV_TAP) + + // init arguments + o->reactor = reactor; + o->handler_error = handler_error; + o->handler_error_user = handler_error_user; + + #ifdef BADVPN_USE_WINAPI + + ASSERT(init_data.init_type == BTAP_INIT_STRING) + + // parse device specification + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specification provided"); + goto fail0; + } + + char *device_component_id; + char *device_name; + uint32_t tun_addrs[3]; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!tapwin32_parse_tun_spec(init_data.init.string, &device_component_id, &device_name, tun_addrs)) { + BLog(BLOG_ERROR, "failed to parse TUN device specification"); + goto fail0; + } + } else { + if (!tapwin32_parse_tap_spec(init_data.init.string, &device_component_id, &device_name)) { + BLog(BLOG_ERROR, "failed to parse TAP device specification"); + goto fail0; + } + } + + // locate device path + + char device_path[TAPWIN32_MAX_REG_SIZE]; + + BLog(BLOG_INFO, "Looking for TAP-Win32 with component ID %s, name %s", device_component_id, device_name); + + if (!tapwin32_find_device(device_component_id, device_name, &device_path)) { + BLog(BLOG_ERROR, "Could not find device"); + goto fail1; + } + + // open device + + BLog(BLOG_INFO, "Opening device %s", device_path); + + o->device = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, 0); + if (o->device == INVALID_HANDLE_VALUE) { + BLog(BLOG_ERROR, "CreateFile failed"); + goto fail1; + } + + // set TUN if needed + + DWORD len; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!DeviceIoControl(o->device, TAP_IOCTL_CONFIG_TUN, tun_addrs, sizeof(tun_addrs), tun_addrs, sizeof(tun_addrs), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed"); + goto fail2; + } + } + + // get MTU + + ULONG umtu; + + if (!DeviceIoControl(o->device, TAP_IOCTL_GET_MTU, NULL, 0, &umtu, sizeof(umtu), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_GET_MTU) failed"); + goto fail2; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = umtu; + } else { + o->frame_mtu = umtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + // set connected + + ULONG upstatus = TRUE; + if (!DeviceIoControl(o->device, TAP_IOCTL_SET_MEDIA_STATUS, &upstatus, sizeof(upstatus), &upstatus, sizeof(upstatus), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_SET_MEDIA_STATUS) failed"); + goto fail2; + } + + BLog(BLOG_INFO, "Device opened"); + + // associate device with IOCP + + if (!CreateIoCompletionPort(o->device, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail2; + } + + // init send olap + BReactorIOCPOverlapped_Init(&o->send_olap, o->reactor, o, NULL); + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv_olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler); + + free(device_name); + free(device_component_id); + + goto success; + +fail2: + ASSERT_FORCE(CloseHandle(o->device)) +fail1: + free(device_name); + free(device_component_id); +fail0: + return 0; + + #endif + + #if defined(BADVPN_LINUX) || defined(BADVPN_FREEBSD) + + o->close_fd = (init_data.init_type != BTAP_INIT_FD); + + switch (init_data.init_type) { + case BTAP_INIT_FD: { + ASSERT(init_data.init.fd.fd >= 0) + ASSERT(init_data.init.fd.mtu >= 0) + ASSERT(init_data.dev_type != BTAP_DEV_TAP || init_data.init.fd.mtu >= BTAP_ETHERNET_HEADER_LENGTH) + + o->fd = init_data.init.fd.fd; + o->frame_mtu = init_data.init.fd.mtu; + } break; + + case BTAP_INIT_STRING: { + char devname_real[IFNAMSIZ]; + + #ifdef BADVPN_LINUX + + // open device + + if ((o->fd = open("/dev/net/tun", O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // configure device + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags |= IFF_NO_PI; + if (init_data.dev_type == BTAP_DEV_TUN) { + ifr.ifr_flags |= IFF_TUN; + } else { + ifr.ifr_flags |= IFF_TAP; + } + if (init_data.init.string) { + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", init_data.init.string); + } + + if (ioctl(o->fd, TUNSETIFF, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + #ifdef BADVPN_FREEBSD + + if (init_data.dev_type == BTAP_DEV_TUN) { + BLog(BLOG_ERROR, "TUN not supported on FreeBSD"); + goto fail0; + } + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specified"); + goto fail0; + } + + // open device + + char devnode[10 + IFNAMSIZ]; + snprintf(devnode, sizeof(devnode), "/dev/%s", init_data.init.string); + + if ((o->fd = open(devnode, O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // get name + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + if (ioctl(o->fd, TAPGIFNAME, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + // get MTU + + // open dummy socket for ioctls + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname_real); + + if (ioctl(sock, SIOCGIFMTU, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error getting MTU"); + close(sock); + goto fail1; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = ifr.ifr_mtu; + } else { + o->frame_mtu = ifr.ifr_mtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + close(sock); + } break; + + default: ASSERT(0); + } + + // set non-blocking + if (fcntl(o->fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init file descriptor object + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + o->poll_events = 0; + + goto success; + +fail1: + if (o->close_fd) { + ASSERT_FORCE(close(o->fd) == 0) + } +fail0: + return 0; + + #endif + +success: + // init output + PacketRecvInterface_Init(&o->output, o->frame_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set no output packet + o->output_packet = NULL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; +} + +void BTap_Free (BTap *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free output + PacketRecvInterface_Free(&o->output); + +#ifdef BADVPN_USE_WINAPI + + // cancel I/O + ASSERT_FORCE(CancelIo(o->device)) + + // wait receiving to finish + if (o->output_packet) { + BLog(BLOG_DEBUG, "waiting for receiving to finish"); + BReactorIOCPOverlapped_Wait(&o->recv_olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv_olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send_olap); + + // close device + ASSERT_FORCE(CloseHandle(o->device)) + +#else + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + if (o->close_fd) { + // close file descriptor + ASSERT_FORCE(close(o->fd) == 0) + } + +#endif +} + +int BTap_GetMTU (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return o->frame_mtu; +} + +void BTap_Send (BTap *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data_len >= 0) + ASSERT(data_len <= o->frame_mtu) + +#ifdef BADVPN_USE_WINAPI + + // ignore frames without an Ethernet header, or we get errors in WriteFile + if (data_len < 14) { + return; + } + + memset(&o->send_olap.olap, 0, sizeof(o->send_olap.olap)); + + // write + BOOL res = WriteFile(o->device, data, data_len, NULL, &o->send_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "WriteFile failed (%u)", GetLastError()); + return; + } + + // wait + int succeeded; + DWORD bytes; + BReactorIOCPOverlapped_Wait(&o->send_olap, &succeeded, &bytes); + + if (!succeeded) { + BLog(BLOG_ERROR, "write operation failed"); + } else { + ASSERT(bytes >= 0) + ASSERT(bytes <= data_len) + + if (bytes < data_len) { + BLog(BLOG_ERROR, "write operation didn't write everything"); + } + } + +#else + + int bytes = write(o->fd, data, data_len); + if (bytes < 0) { + // malformed packets will cause errors, ignore them and act like + // the packet was accepeted + } else { + if (bytes != data_len) { + BLog(BLOG_WARNING, "written %d expected %d", bytes, data_len); + } + } + +#endif +} + +PacketRecvInterface * BTap_GetOutput (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/app/src/main/jni/badvpn/tuntap/BTap.h b/app/src/main/jni/badvpn/tuntap/BTap.h new file mode 100644 index 0000000..021ac83 --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/BTap.h @@ -0,0 +1,196 @@ +/** + * @file BTap.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * TAP device abstraction. + */ + +#ifndef BADVPN_TUNTAP_BTAP_H +#define BADVPN_TUNTAP_BTAP_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_LINUX) + defined(BADVPN_FREEBSD)) != 1 +#error Unknown TAP backend or too many TAP backends +#endif + +#include + +#ifdef BADVPN_USE_WINAPI +#else +#include +#endif + +#include +#include +#include +#include +#include + +#define BTAP_ETHERNET_HEADER_LENGTH 14 + +/** + * Handler called when an error occurs on the device. + * The object must be destroyed from the job context of this + * handler, and no further I/O may occur. + * + * @param user as in {@link BTap_Init} + */ +typedef void (*BTap_handler_error) (void *used); + +typedef struct { + BReactor *reactor; + BTap_handler_error handler_error; + void *handler_error_user; + int frame_mtu; + PacketRecvInterface output; + uint8_t *output_packet; + +#ifdef BADVPN_USE_WINAPI + HANDLE device; + BReactorIOCPOverlapped send_olap; + BReactorIOCPOverlapped recv_olap; +#else + int close_fd; + int fd; + BFileDescriptor bfd; + int poll_events; +#endif + + DebugError d_err; + DebugObject d_obj; +} BTap; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param devname name of the devece to open. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying a NULL devname is equivalent to specifying ":". + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @param tun whether to create a TUN (IP) device or a TAP (Ethernet) device. Must be 0 or 1. + * @return 1 on success, 0 on failure + */ +int BTap_Init (BTap *o, BReactor *bsys, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) WARN_UNUSED; + +enum BTap_dev_type {BTAP_DEV_TUN, BTAP_DEV_TAP}; + +enum BTap_init_type { + BTAP_INIT_STRING, +#ifndef BADVPN_USE_WINAPI + BTAP_INIT_FD, +#endif +}; + +struct BTap_init_data { + enum BTap_dev_type dev_type; + enum BTap_init_type init_type; + union { + char *string; + struct { + int fd; + int mtu; + } fd; + } init; +}; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param init_data struct containing initialization parameters (to allow transparent passing). + * init.data.dev_type must be either BTAP_DEV_TUN for an IP device, or + * BTAP_DEV_TAP for an Ethernet device. + * init_data.init_type must be either BTAP_INIT_STRING or BTAP_INIT_FD. + * For BTAP_INIT_STRING, init_data.init.string specifies the TUN or TAP + * device, as described next. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying NULL is equivalent to specifying ":". + * For BTAP_INIT_FD, the device is initialized using a file descriptor. + * In this case, init_data.init.fd.fd must be set to the file descriptor, + * and init_data.init.fd.mtu must be set to the largest IP packet or + * Ethernet frame supported, for a TUN or TAP device, respectively. + * File descriptor initialization is not supported on Windows. + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @return 1 on success, 0 on failure + */ +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) WARN_UNUSED; + +/** + * Frees the TAP device. + * + * @param o the object + */ +void BTap_Free (BTap *o); + +/** + * Returns the device's maximum transmission unit (including any protocol headers). + * + * @param o the object + * @return device's MTU + */ +int BTap_GetMTU (BTap *o); + +/** + * Sends a packet to the device. + * Any errors will be reported via a job. + * + * @param o the object + * @param data packet to send + * @param data_len length of packet. Must be >=0 and <=MTU, as reported by {@link BTap_GetMTU}. + */ +void BTap_Send (BTap *o, uint8_t *data, int data_len); + +/** + * Returns a {@link PacketRecvInterface} for reading packets from the device. + * The MTU of the interface will be {@link BTap_GetMTU}. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BTap_GetOutput (BTap *o); + +#endif diff --git a/app/src/main/jni/badvpn/tuntap/CMakeLists.txt b/app/src/main/jni/badvpn/tuntap/CMakeLists.txt new file mode 100644 index 0000000..fd451c1 --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/CMakeLists.txt @@ -0,0 +1,10 @@ +set(TUNTAP_ADDITIONAL_SOURCES) +if (WIN32) + list(APPEND TUNTAP_ADDITIONAL_SOURCES tapwin32-funcs.c) +endif () + +set(TUNTAP_SOURCES + BTap.c + ${TUNTAP_ADDITIONAL_SOURCES} +) +badvpn_add_library(tuntap "system;flow" "" "${TUNTAP_SOURCES}") diff --git a/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.c b/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.c new file mode 100644 index 0000000..37ce05d --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.c @@ -0,0 +1,227 @@ +/** + * @file tapwin32-funcs.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "wintap-common.h" + +#include + +static int split_spec (char *name, char *sep, char **out_fields[], int num_fields) +{ + ASSERT(num_fields > 0) + ASSERT(strlen(sep) > 0) + + size_t seplen = strlen(sep); + + int i = 0; + while (i < num_fields - 1) { + char *s = strstr(name, sep); + if (!s) { + DEBUG("missing separator number %d", (i + 1)); + goto fail; + } + + if (!(*out_fields[i] = b_strdup_bin(name, s - name))) { + DEBUG("b_strdup_bin failed"); + goto fail; + } + + name = s + seplen; + i++; + } + + if (!(*out_fields[i] = b_strdup(name))) { + DEBUG("b_strdup_bin failed"); + goto fail; + } + + return 1; + +fail: + while (i-- > 0) { + free(*out_fields[i]); + } + return 0; +} + +int tapwin32_parse_tap_spec (char *name, char **out_component_id, char **out_human_name) +{ + char **out_fields[2]; + out_fields[0] = out_component_id; + out_fields[1] = out_human_name; + + return split_spec(name, ":", out_fields, 2); +} + +int tapwin32_parse_tun_spec (char *name, char **out_component_id, char **out_human_name, uint32_t out_addrs[3]) +{ + char *addr_strs[3]; + + char **out_fields[5]; + out_fields[0] = out_component_id; + out_fields[1] = out_human_name; + out_fields[2] = &addr_strs[0]; + out_fields[3] = &addr_strs[1]; + out_fields[4] = &addr_strs[2]; + + if (!split_spec(name, ":", out_fields, 5)) { + goto fail0; + } + + for (int i = 0; i < 3; i++) { + if (!ipaddr_parse_ipv4_addr(addr_strs[i], &out_addrs[i])) { + goto fail1; + } + } + + free(addr_strs[0]); + free(addr_strs[1]); + free(addr_strs[2]); + + return 1; + +fail1: + free(*out_component_id); + free(*out_human_name); + free(addr_strs[0]); + free(addr_strs[1]); + free(addr_strs[2]); +fail0: + return 0; +} + +int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]) +{ + // open adapter key + // used to find all devices with the given ComponentId + HKEY adapter_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &adapter_key) != ERROR_SUCCESS) { + DEBUG("Error opening adapter key"); + return 0; + } + + char net_cfg_instance_id[TAPWIN32_MAX_REG_SIZE]; + int found = 0; + int pres; + + DWORD i; + for (i = 0;; i++) { + DWORD len; + DWORD type; + + char key_name[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(key_name); + if (RegEnumKeyEx(adapter_key, i, key_name, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { + break; + } + + char unit_string[TAPWIN32_MAX_REG_SIZE]; + pres = _snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, key_name); + if (pres < 0 || pres == sizeof(unit_string)) { + continue; + } + HKEY unit_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key) != ERROR_SUCCESS) { + continue; + } + + char component_id[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(component_id); + if (RegQueryValueEx(unit_key, "ComponentId", NULL, &type, (LPBYTE)component_id, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS) + continue; + } + + len = sizeof(net_cfg_instance_id); + if (RegQueryValueEx(unit_key, "NetCfgInstanceId", NULL, &type, (LPBYTE)net_cfg_instance_id, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS) + continue; + } + + RegCloseKey(unit_key); + + // check if ComponentId matches + if (!strcmp(component_id, device_component_id)) { + // if no name was given, use the first device with the given ComponentId + if (!device_name) { + found = 1; + break; + } + + // open connection key + char conn_string[TAPWIN32_MAX_REG_SIZE]; + pres = _snprintf(conn_string, sizeof(conn_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, net_cfg_instance_id); + if (pres < 0 || pres == sizeof(conn_string)) { + continue; + } + HKEY conn_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, conn_string, 0, KEY_READ, &conn_key) != ERROR_SUCCESS) { + continue; + } + + // read name + char name[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(name); + if (RegQueryValueEx(conn_key, "Name", NULL, &type, (LPBYTE)name, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS) + continue; + } + + ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS) + + // check name + if (!strcmp(name, device_name)) { + found = 1; + break; + } + } + } + + ASSERT_FORCE(RegCloseKey(adapter_key) == ERROR_SUCCESS) + + if (!found) { + return 0; + } + + pres = _snprintf(*device_path, sizeof(*device_path), "\\\\.\\Global\\%s.tap", net_cfg_instance_id); + if (pres < 0 || pres == sizeof(*device_path)) { + return 0; + } + + return 1; +} diff --git a/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.h b/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.h new file mode 100644 index 0000000..fed66de --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/tapwin32-funcs.h @@ -0,0 +1,42 @@ +/** + * @file tapwin32-funcs.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUNTAP_TAPWIN32_FUNCS_H +#define BADVPN_TUNTAP_TAPWIN32_FUNCS_H + +#include +#include + +#define TAPWIN32_MAX_REG_SIZE 256 + +int tapwin32_parse_tap_spec (char *name, char **out_component_id, char **out_human_name); +int tapwin32_parse_tun_spec (char *name, char **out_component_id, char **out_human_name, uint32_t out_addrs[3]); +int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]); + +#endif diff --git a/app/src/main/jni/badvpn/tuntap/wintap-common.h b/app/src/main/jni/badvpn/tuntap/wintap-common.h new file mode 100644 index 0000000..922c851 --- /dev/null +++ b/app/src/main/jni/badvpn/tuntap/wintap-common.h @@ -0,0 +1,39 @@ +/** + * @file wintap-common.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * API definitions for TAP-Win32. + */ + +#define TAP_IOCTL_CONFIG_TUN CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_GET_MTU CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_SET_MEDIA_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" diff --git a/app/src/main/jni/badvpn/udevmonitor/CMakeLists.txt b/app/src/main/jni/badvpn/udevmonitor/CMakeLists.txt new file mode 100644 index 0000000..be1917a --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(udevmonitor + NCDUdevMonitorParser.c + NCDUdevMonitor.c + NCDUdevCache.c + NCDUdevManager.c +) +target_link_libraries(udevmonitor system flow stringmap) diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.c b/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.c new file mode 100644 index 0000000..cb88821 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.c @@ -0,0 +1,417 @@ +/** + * @file NCDUdevCache.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +static int string_comparator (void *unused, const char **str1, const char **str2) +{ + int c = strcmp(*str1, *str2); + return B_COMPARE(c, 0); +} + +static void free_device (NCDUdevCache *o, struct NCDUdevCache_device *device) +{ + if (device->is_cleaned) { + // remove from cleaned devices list + LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + } else { + // remove from devices tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + } + + // free map + BStringMap_Free(&device->map); + + // free structure + free(device); +} + +static struct NCDUdevCache_device * lookup_device (NCDUdevCache *o, const char *devpath) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->devices_tree, &devpath); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device; +} + +static void rename_devices (NCDUdevCache *o, const char *prefix, const char *new_prefix) +{ + ASSERT(strlen(prefix) > 0) + + size_t prefix_len = strlen(prefix); + + // lookup prefix + BAVLNode *tree_node = BAVL_Lookup(&o->devices_tree, &prefix); + if (!tree_node) { + return; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // if the result does not begin with prefix, we might gave gotten the device before all + // devices beginning with prefix, so skip it + if (!string_begins_with(device->devpath, prefix)) { + tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + } + + while (tree_node) { + // get next node (must be here because we rename this device) + BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + + // get device + device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // if it doesn't begin with prefix, we're done + if (!string_begins_with(device->devpath, prefix)) { + break; + } + + // build new devpath + char *new_devpath = concat_strings(2, new_prefix, device->devpath + prefix_len); + if (!new_devpath) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_loop0; + } + + // make sure the new name does not exist + if (BAVL_LookupExact(&o->devices_tree, &new_devpath)) { + BLog(BLOG_ERROR, "rename destination already exists"); + goto fail_loop1; + } + + BLog(BLOG_DEBUG, "rename %s -> %s", device->devpath, new_devpath); + + // remove from tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + + // update devpath in map + if (!BStringMap_Set(&device->map, "DEVPATH", new_devpath)) { + BLog(BLOG_ERROR, "BStringMap_Set failed"); + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + goto fail_loop1; + } + + // update devpath pointer + device->devpath = BStringMap_Get(&device->map, "DEVPATH"); + ASSERT(device->devpath) + + // insert to tree + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + + fail_loop1: + free(new_devpath); + fail_loop0: + tree_node = next_tree_node; + } +} + +static int add_device (NCDUdevCache *o, BStringMap map) +{ + ASSERT(BStringMap_Get(&map, "DEVPATH")) + + // alloc structure + struct NCDUdevCache_device *device = malloc(sizeof(*device)); + if (!device) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init map + device->map = map; + + // set device path + device->devpath = BStringMap_Get(&device->map, "DEVPATH"); + + // insert to devices tree + BAVLNode *ex_node; + if (!BAVL_Insert(&o->devices_tree, &device->devices_tree_node, &ex_node)) { + BLog(BLOG_DEBUG, "update %s", device->devpath); + + // get existing device + struct NCDUdevCache_device *ex_device = UPPER_OBJECT(ex_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!ex_device->is_cleaned) + + // remove exiting device + free_device(o, ex_device); + + // insert + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + } else { + BLog(BLOG_DEBUG, "add %s", device->devpath); + } + + // set not cleaned + device->is_cleaned = 0; + + // set refreshed + device->is_refreshed = 1; + + return 1; + +fail0: + return 0; +} + +void NCDUdevCache_Init (NCDUdevCache *o) +{ + // init devices tree + BAVL_Init(&o->devices_tree, OFFSET_DIFF(struct NCDUdevCache_device, devpath, devices_tree_node), (BAVL_comparator)string_comparator, NULL); + + // init cleaned devices list + LinkedList1_Init(&o->cleaned_devices_list); + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevCache_Free (NCDUdevCache *o) +{ + DebugObject_Free(&o->d_obj); + + // free cleaned devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->cleaned_devices_list)) { + struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node); + ASSERT(device->is_cleaned) + free_device(o, device); + } + + // free devices + BAVLNode *tree_node; + while (tree_node = BAVL_GetFirst(&o->devices_tree)) { + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + free_device(o, device); + } +} + +const BStringMap * NCDUdevCache_Query (NCDUdevCache *o, const char *devpath) +{ + DebugObject_Access(&o->d_obj); + + // lookup device + struct NCDUdevCache_device *device = lookup_device(o, devpath); + if (!device) { + return NULL; + } + + // return map + return &device->map; +} + +int NCDUdevCache_Event (NCDUdevCache *o, BStringMap map) +{ + DebugObject_Access(&o->d_obj); + + // get device path + const char *devpath = BStringMap_Get(&map, "DEVPATH"); + if (!devpath) { + BLog(BLOG_ERROR, "missing DEVPATH"); + goto fail; + } + + // get action + const char *action = BStringMap_Get(&map, "ACTION"); + + // if this is a remove event, remove device if we have it + if (action && !strcmp(action, "remove")) { + // remove existing device + struct NCDUdevCache_device *device = lookup_device(o, devpath); + if (device) { + BLog(BLOG_DEBUG, "remove %s", devpath); + free_device(o, device); + } else { + BLog(BLOG_DEBUG, "remove unknown %s", devpath); + } + + // eat map + BStringMap_Free(&map); + + return 1; + } + + // if this is a move event, remove old device and contaned devices + if (action && !strcmp(action, "move")) { + const char *devpath_old = BStringMap_Get(&map, "DEVPATH_OLD"); + if (!devpath_old) { + goto fail_rename0; + } + + // remove old device + struct NCDUdevCache_device *old_device = lookup_device(o, devpath_old); + if (old_device) { + BLog(BLOG_DEBUG, "remove moved %s", old_device->devpath); + free_device(o, old_device); + } + + // construct prefix "/" and new prefix "/" + char *prefix = concat_strings(2, devpath_old, "/"); + if (!prefix) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_rename0;; + } + char *new_prefix = concat_strings(2, devpath, "/"); + if (!new_prefix) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_rename1; + } + + // rename devices with paths starting with prefix + rename_devices(o, prefix, new_prefix); + + free(new_prefix); + fail_rename1: + free(prefix); + fail_rename0:; + } + + // add device + if (!add_device(o, map)) { + BLog(BLOG_DEBUG, "failed to add device %s", devpath); + goto fail; + } + + return 1; + +fail: + return 0; +} + +void NCDUdevCache_StartClean (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + // mark all devices not refreshed + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + while (tree_node) { + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // set device not refreshed + device->is_refreshed = 0; + + tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + } +} + +void NCDUdevCache_FinishClean (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + // move all devices not marked refreshed to the cleaned devices list + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + while (tree_node) { + BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + if (!device->is_refreshed) { + BLog(BLOG_DEBUG, "clean %s", device->devpath); + + // remove from devices tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + + // insert to cleaned devices list + LinkedList1_Append(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + + // set device cleaned + device->is_cleaned = 1; + } + + tree_node = next_tree_node; + } +} + +int NCDUdevCache_GetCleanedDevice (NCDUdevCache *o, BStringMap *out_map) +{ + DebugObject_Access(&o->d_obj); + + // get cleaned device + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->cleaned_devices_list); + if (!list_node) { + return 0; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node); + ASSERT(device->is_cleaned) + + // remove from cleaned devices list + LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + + // give away map + *out_map = device->map; + + // free structure + free(device); + + return 1; +} + +const char * NCDUdevCache_First (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device->devpath; +} + +const char * NCDUdevCache_Next (NCDUdevCache *o, const char *key) +{ + ASSERT(BAVL_LookupExact(&o->devices_tree, &key)) + + BAVLNode *tree_node = BAVL_GetNext(&o->devices_tree, BAVL_LookupExact(&o->devices_tree, &key)); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device->devpath; +} diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.h b/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.h new file mode 100644 index 0000000..2d9e8d8 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevCache.h @@ -0,0 +1,66 @@ +/** + * @file NCDUdevCache.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVCACHE_H +#define BADVPN_UDEVMONITOR_NCDUDEVCACHE_H + +#include +#include +#include +#include +#include + +struct NCDUdevCache_device { + BStringMap map; + const char *devpath; + int is_cleaned; + union { + BAVLNode devices_tree_node; + LinkedList1Node cleaned_devices_list_node; + }; + int is_refreshed; +}; + +typedef struct { + BAVL devices_tree; + LinkedList1 cleaned_devices_list; + DebugObject d_obj; +} NCDUdevCache; + +void NCDUdevCache_Init (NCDUdevCache *o); +void NCDUdevCache_Free (NCDUdevCache *o); +const BStringMap * NCDUdevCache_Query (NCDUdevCache *o, const char *devpath); +int NCDUdevCache_Event (NCDUdevCache *o, BStringMap map) WARN_UNUSED; +void NCDUdevCache_StartClean (NCDUdevCache *o); +void NCDUdevCache_FinishClean (NCDUdevCache *o); +int NCDUdevCache_GetCleanedDevice (NCDUdevCache *o, BStringMap *out_map); +const char * NCDUdevCache_First (NCDUdevCache *o); +const char * NCDUdevCache_Next (NCDUdevCache *o, const char *key); + +#endif diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.c b/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.c new file mode 100644 index 0000000..4a44fb7 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.c @@ -0,0 +1,547 @@ +/** + * @file NCDUdevManager.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +#include + +#define RESTART_TIMER_TIME 5000 + +static int event_to_map (NCDUdevMonitor *monitor, BStringMap *out_map); +static void free_event (NCDUdevClient *o, struct NCDUdevClient_event *e); +static void queue_event (NCDUdevManager *o, NCDUdevMonitor *monitor, NCDUdevClient *client); +static void queue_mapless_event (NCDUdevManager *o, const char *devpath, NCDUdevClient *client); +static void process_event (NCDUdevManager *o, NCDUdevMonitor *monitor); +static void try_monitor (NCDUdevManager *o); +static void reset_monitor (NCDUdevManager *o); +static void timer_handler (NCDUdevManager *o); +static void monitor_handler_event (NCDUdevManager *o); +static void monitor_handler_error (NCDUdevManager *o, int is_error); +static void info_monitor_handler_event (NCDUdevManager *o); +static void info_monitor_handler_error (NCDUdevManager *o, int is_error); +static void next_job_handler (NCDUdevClient *o); + +static int event_to_map (NCDUdevMonitor *monitor, BStringMap *out_map) +{ + NCDUdevMonitor_AssertReady(monitor); + + // init map + BStringMap_Init(out_map); + + // insert properties to map + int num_properties = NCDUdevMonitor_GetNumProperties(monitor); + for (int i = 0; i < num_properties; i++) { + const char *name; + const char *value; + NCDUdevMonitor_GetProperty(monitor, i, &name, &value); + + if (!BStringMap_Set(out_map, name, value)) { + BLog(BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + } + + return 1; + +fail1: + BStringMap_Free(out_map); + return 0; +} + +static void free_event (NCDUdevClient *o, struct NCDUdevClient_event *e) +{ + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // free map + if (e->have_map) { + BStringMap_Free(&e->map); + } + + // free devpath + free(e->devpath); + + // free structure + free(e); +} + +static void queue_event (NCDUdevManager *o, NCDUdevMonitor *monitor, NCDUdevClient *client) +{ + NCDUdevMonitor_AssertReady(monitor); + + // alloc event + struct NCDUdevClient_event *e = malloc(sizeof(*e)); + if (!e) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // build map + if (!event_to_map(monitor, &e->map)) { + goto fail1; + } + + // set have map + e->have_map = 1; + + // get devpath + const char *devpath = BStringMap_Get(&e->map, "DEVPATH"); + if (!devpath) { + BLog(BLOG_ERROR, "DEVPATH missing"); + goto fail2; + } + + // copy devpath + if (!(e->devpath = strdup(devpath))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // insert to client's events list + LinkedList1_Append(&client->events_list, &e->events_list_node); + + // if client is running, set next job + if (client->running) { + BPending_Set(&client->next_job); + } + + return; + +fail2: + BStringMap_Free(&e->map); +fail1: + free(e); +fail0: + return; +} + +static void queue_mapless_event (NCDUdevManager *o, const char *devpath, NCDUdevClient *client) +{ + // alloc event + struct NCDUdevClient_event *e = malloc(sizeof(*e)); + if (!e) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set have no map + e->have_map = 0; + + // copy devpath + if (!(e->devpath = strdup(devpath))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // insert to client's events list + LinkedList1_Append(&client->events_list, &e->events_list_node); + + // if client is running, set next job + if (client->running) { + BPending_Set(&client->next_job); + } + + return; + +fail1: + free(e); +fail0: + return; +} + +static void process_event (NCDUdevManager *o, NCDUdevMonitor *monitor) +{ + NCDUdevMonitor_AssertReady(monitor); + + // build map from event + BStringMap map; + if (!event_to_map(monitor, &map)) { + BLog(BLOG_ERROR, "failed to build map"); + return; + } + + // pass event to cache + if (!NCDUdevCache_Event(&o->cache, map)) { + BLog(BLOG_ERROR, "failed to cache"); + BStringMap_Free(&map); + return; + } + + // queue event to clients + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->clients_list); + while (list_node) { + NCDUdevClient *client = UPPER_OBJECT(list_node, NCDUdevClient, clients_list_node); + queue_event(o, monitor, client); + list_node = LinkedList1Node_Next(list_node); + } +} + +static void try_monitor (NCDUdevManager *o) +{ + ASSERT(!o->have_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + int mode = (o->no_udev ? NCDUDEVMONITOR_MODE_MONITOR_KERNEL : NCDUDEVMONITOR_MODE_MONITOR_UDEV); + + // init monitor + if (!NCDUdevMonitor_Init(&o->monitor, o->reactor, o->manager, mode, o, + (NCDUdevMonitor_handler_event)monitor_handler_event, + (NCDUdevMonitor_handler_error)monitor_handler_error + )) { + BLog(BLOG_ERROR, "NCDUdevMonitor_Init failed"); + + // set restart timer + BReactor_SetTimer(o->reactor, &o->restart_timer); + return; + } + + // set have monitor + o->have_monitor = 1; + + // set not have info monitor + o->have_info_monitor = 0; +} + +static void reset_monitor (NCDUdevManager *o) +{ + ASSERT(o->have_monitor) + ASSERT(!o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + // free monitor + NCDUdevMonitor_Free(&o->monitor); + + // set have no monitor + o->have_monitor = 0; + + // set restart timer + BReactor_SetTimer(o->reactor, &o->restart_timer); +} + +static void timer_handler (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_monitor) + + // try again + try_monitor(o); +} + +static void monitor_handler_event (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(!o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + if (NCDUdevMonitor_IsReadyEvent(&o->monitor)) { + BLog(BLOG_INFO, "monitor ready"); + + // init info monitor + if (!NCDUdevMonitor_Init(&o->info_monitor, o->reactor, o->manager, NCDUDEVMONITOR_MODE_INFO, o, + (NCDUdevMonitor_handler_event)info_monitor_handler_event, + (NCDUdevMonitor_handler_error)info_monitor_handler_error + )) { + BLog(BLOG_ERROR, "NCDUdevMonitor_Init failed"); + reset_monitor(o); + return; + } + + // set have info monitor + o->have_info_monitor = 1; + + // start cache cleanup + NCDUdevCache_StartClean(&o->cache); + + // hold processing monitor events until info monitor is done + return; + } + + // accept event + NCDUdevMonitor_Done(&o->monitor); + + // process event + process_event(o, &o->monitor); +} + +static void monitor_handler_error (NCDUdevManager *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + BLog(BLOG_ERROR, "monitor error"); + + if (o->have_info_monitor) { + // free info monitor + NCDUdevMonitor_Free(&o->info_monitor); + + // set have no info monitor + o->have_info_monitor = 0; + } + + // reset monitor + reset_monitor(o); +} + +static void info_monitor_handler_event (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + // accept event + NCDUdevMonitor_Done(&o->info_monitor); + + // process event + process_event(o, &o->info_monitor); +} + +static void info_monitor_handler_error (NCDUdevManager *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + if (is_error) { + BLog(BLOG_ERROR, "info monitor error"); + } else { + BLog(BLOG_INFO, "info monitor finished"); + } + + // free info monitor + NCDUdevMonitor_Free(&o->info_monitor); + + // set have no info monitor + o->have_info_monitor = 0; + + if (is_error) { + // reset monitor + reset_monitor(o); + } else { + // continue processing monitor events + NCDUdevMonitor_Done(&o->monitor); + + // finish cache cleanup + NCDUdevCache_FinishClean(&o->cache); + + // collect cleaned devices + BStringMap map; + while (NCDUdevCache_GetCleanedDevice(&o->cache, &map)) { + // get devpath + const char *devpath = BStringMap_Get(&map, "DEVPATH"); + ASSERT(devpath) + + // queue mapless event to clients + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->clients_list); + while (list_node) { + NCDUdevClient *client = UPPER_OBJECT(list_node, NCDUdevClient, clients_list_node); + queue_mapless_event(o, devpath, client); + list_node = LinkedList1Node_Next(list_node); + } + + BStringMap_Free(&map); + } + } +} + +static void next_job_handler (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!LinkedList1_IsEmpty(&o->events_list)) + ASSERT(o->running) + + // get event + struct NCDUdevClient_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->events_list), struct NCDUdevClient_event, events_list_node); + + // grab map from event + int have_map = e->have_map; + BStringMap map = e->map; + + // grab devpath from event + char *devpath = e->devpath; + + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // free structure + free(e); + + // schedule next event if needed + if (!LinkedList1_IsEmpty(&o->events_list)) { + BPending_Set(&o->next_job); + } + + // give map to handler + o->handler(o->user, devpath, have_map, map); + return; +} + +void NCDUdevManager_Init (NCDUdevManager *o, int no_udev, BReactor *reactor, BProcessManager *manager) +{ + ASSERT(no_udev == 0 || no_udev == 1) + + // init arguments + o->no_udev = no_udev; + o->reactor = reactor; + o->manager = manager; + + // init clients list + LinkedList1_Init(&o->clients_list); + + // init cache + NCDUdevCache_Init(&o->cache); + + // init restart timer + BTimer_Init(&o->restart_timer, RESTART_TIMER_TIME, (BTimer_handler)timer_handler, o); + + // set have no monitor + o->have_monitor = 0; + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevManager_Free (NCDUdevManager *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(LinkedList1_IsEmpty(&o->clients_list)) + + if (o->have_monitor) { + // free info monitor + if (o->have_info_monitor) { + NCDUdevMonitor_Free(&o->info_monitor); + } + + // free monitor + NCDUdevMonitor_Free(&o->monitor); + } + + // free restart timer + BReactor_RemoveTimer(o->reactor, &o->restart_timer); + + // free cache + NCDUdevCache_Free(&o->cache); +} + +const BStringMap * NCDUdevManager_Query (NCDUdevManager *o, const char *devpath) +{ + DebugObject_Access(&o->d_obj); + + return NCDUdevCache_Query(&o->cache, devpath); +} + +void NCDUdevClient_Init (NCDUdevClient *o, NCDUdevManager *m, void *user, + NCDUdevClient_handler handler) +{ + DebugObject_Access(&m->d_obj); + + // init arguments + o->m = m; + o->user = user; + o->handler = handler; + + // insert to manager's list + LinkedList1_Append(&m->clients_list, &o->clients_list_node); + + // init events list + LinkedList1_Init(&o->events_list); + + // init next job + BPending_Init(&o->next_job, BReactor_PendingGroup(m->reactor), (BPending_handler)next_job_handler, o); + + // set running + o->running = 1; + + // queue all devices from cache + const char *devpath = NCDUdevCache_First(&m->cache); + while (devpath) { + queue_mapless_event(m, devpath, o); + devpath = NCDUdevCache_Next(&m->cache, devpath); + } + + // if this is the first client, init monitor + if (!m->have_monitor && !BTimer_IsRunning(&m->restart_timer)) { + try_monitor(m); + } + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevClient_Free (NCDUdevClient *o) +{ + DebugObject_Free(&o->d_obj); + NCDUdevManager *m = o->m; + + // free events + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->events_list)) { + struct NCDUdevClient_event *e = UPPER_OBJECT(list_node, struct NCDUdevClient_event, events_list_node); + free_event(o, e); + } + + // free next job + BPending_Free(&o->next_job); + + // remove from manager's list + LinkedList1_Remove(&m->clients_list, &o->clients_list_node); +} + +void NCDUdevClient_Pause (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->running) + + // set not running + o->running = 0; + + // unset next job to avoid reporting queued events + BPending_Unset(&o->next_job); +} + +void NCDUdevClient_Continue (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->running) + + // set running + o->running = 1; + + // set next job if we have events queued + if (!LinkedList1_IsEmpty(&o->events_list)) { + BPending_Set(&o->next_job); + } +} diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.h b/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.h new file mode 100644 index 0000000..26ecfc6 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevManager.h @@ -0,0 +1,84 @@ +/** + * @file NCDUdevManager.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMANAGER_H +#define BADVPN_UDEVMONITOR_NCDUDEVMANAGER_H + +#include +#include +#include +#include +#include +#include + +typedef void (*NCDUdevClient_handler) (void *user, char *devpath, int have_map, BStringMap map); + +typedef struct { + int no_udev; + BReactor *reactor; + BProcessManager *manager; + LinkedList1 clients_list; + NCDUdevCache cache; + BTimer restart_timer; + int have_monitor; + NCDUdevMonitor monitor; + int have_info_monitor; + NCDUdevMonitor info_monitor; + DebugObject d_obj; +} NCDUdevManager; + +typedef struct { + NCDUdevManager *m; + void *user; + NCDUdevClient_handler handler; + LinkedList1Node clients_list_node; + LinkedList1 events_list; + BPending next_job; + int running; + DebugObject d_obj; +} NCDUdevClient; + +struct NCDUdevClient_event { + char *devpath; + int have_map; + BStringMap map; + LinkedList1Node events_list_node; +}; + +void NCDUdevManager_Init (NCDUdevManager *o, int no_udev, BReactor *reactor, BProcessManager *manager); +void NCDUdevManager_Free (NCDUdevManager *o); +const BStringMap * NCDUdevManager_Query (NCDUdevManager *o, const char *devpath); + +void NCDUdevClient_Init (NCDUdevClient *o, NCDUdevManager *m, void *user, + NCDUdevClient_handler handler); +void NCDUdevClient_Free (NCDUdevClient *o); +void NCDUdevClient_Pause (NCDUdevClient *o); +void NCDUdevClient_Continue (NCDUdevClient *o); + +#endif diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.c b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.c new file mode 100644 index 0000000..56970cd --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.c @@ -0,0 +1,250 @@ +/** + * @file NCDUdevMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include + +#define PARSER_BUF_SIZE 16384 +#define PARSER_MAX_PROPERTIES 256 + +static void report_error (NCDUdevMonitor *o) +{ + ASSERT(!o->process_running) + ASSERT(!o->input_running) + + DEBUGERROR(&o->d_err, o->handler_error(o->user, (o->process_was_error || o->input_was_error))); +} + +static void process_handler_terminated (NCDUdevMonitor *o, int normally, uint8_t normally_exit_status) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->process_running) + + BLog(BLOG_INFO, "process terminated"); + + // set process not running (so we don't try to kill it) + o->process_running = 0; + + // remember process error + o->process_was_error = !(normally && normally_exit_status == 0); + + if (!o->input_running) { + report_error(o); + return; + } +} + +static void process_handler_closed (NCDUdevMonitor *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->input_running) + + if (is_error) { + BLog(BLOG_ERROR, "pipe error"); + } else { + BLog(BLOG_INFO, "pipe closed"); + } + + // disconnect connector + StreamRecvConnector_DisconnectInput(&o->connector); + + // set input not running + o->input_running = 0; + + // remember input error + o->input_was_error = is_error; + + if (!o->process_running) { + report_error(o); + return; + } +} + +static void parser_handler (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + o->handler_event(o->user); + return; +} + +int NCDUdevMonitor_Init (NCDUdevMonitor *o, BReactor *reactor, BProcessManager *manager, int mode, void *user, + NCDUdevMonitor_handler_event handler_event, + NCDUdevMonitor_handler_error handler_error) +{ + ASSERT(mode == NCDUDEVMONITOR_MODE_MONITOR_UDEV || mode == NCDUDEVMONITOR_MODE_INFO || mode == NCDUDEVMONITOR_MODE_MONITOR_KERNEL) + + // init arguments + o->user = user; + o->handler_event = handler_event; + o->handler_error = handler_error; + + // find programs + char *stdbuf_exec = badvpn_find_program("stdbuf"); + char *udevadm_exec = badvpn_find_program("udevadm"); + if (!stdbuf_exec) { + BLog(BLOG_ERROR, "failed to find stdbuf program"); + goto fail0; + } + if (!udevadm_exec) { + BLog(BLOG_ERROR, "failed to find udevadm program"); + goto fail0; + } + + // construct arguments + const char *argv_monitor_udev[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--udev", "--environment", NULL}; + const char *argv_monitor_kernel[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--kernel", "--environment", NULL}; + const char *argv_info[] = {stdbuf_exec, "-o", "L", udevadm_exec, "info", "--query", "all", "--export-db", NULL}; + + // choose arguments based on mode + const char **argv = NULL; // to remove warning + switch (mode) { + case NCDUDEVMONITOR_MODE_MONITOR_UDEV: argv = argv_monitor_udev; break; + case NCDUDEVMONITOR_MODE_INFO: argv = argv_info; break; + case NCDUDEVMONITOR_MODE_MONITOR_KERNEL: argv = argv_monitor_kernel; break; + default: ASSERT(0); + } + + // init process + if (!BInputProcess_Init(&o->process, reactor, manager, o, + (BInputProcess_handler_terminated)process_handler_terminated, + (BInputProcess_handler_closed)process_handler_closed + )) { + BLog(BLOG_ERROR, "BInputProcess_Init failed"); + goto fail0; + } + + // init connector + StreamRecvConnector_Init(&o->connector, BReactor_PendingGroup(reactor)); + StreamRecvConnector_ConnectInput(&o->connector, BInputProcess_GetInput(&o->process)); + + // init parser + if (!NCDUdevMonitorParser_Init(&o->parser, StreamRecvConnector_GetOutput(&o->connector), PARSER_BUF_SIZE, PARSER_MAX_PROPERTIES, + (mode == NCDUDEVMONITOR_MODE_INFO), BReactor_PendingGroup(reactor), o, + (NCDUdevMonitorParser_handler)parser_handler + )) { + BLog(BLOG_ERROR, "NCDUdevMonitorParser_Init failed"); + goto fail1; + } + + // start process + if (!BInputProcess_Start(&o->process, stdbuf_exec, (char **)argv, NULL)) { + BLog(BLOG_ERROR, "BInputProcess_Start failed"); + goto fail2; + } + + // set process running, input running + o->process_running = 1; + o->input_running = 1; + + free(udevadm_exec); + free(stdbuf_exec); + + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + NCDUdevMonitorParser_Free(&o->parser); +fail1: + StreamRecvConnector_Free(&o->connector); + BInputProcess_Free(&o->process); +fail0: + free(udevadm_exec); + free(stdbuf_exec); + return 0; +} + +void NCDUdevMonitor_Free (NCDUdevMonitor *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free parser + NCDUdevMonitorParser_Free(&o->parser); + + // free connector + StreamRecvConnector_Free(&o->connector); + + // kill process it it's running + if (o->process_running) { + BInputProcess_Kill(&o->process); + } + + // free process + BInputProcess_Free(&o->process); +} + +void NCDUdevMonitor_Done (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + NCDUdevMonitorParser_Done(&o->parser); +} + +int NCDUdevMonitor_IsReadyEvent (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + return NCDUdevMonitorParser_IsReadyEvent(&o->parser); +} +void NCDUdevMonitor_AssertReady (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); +} + +int NCDUdevMonitor_GetNumProperties (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + return NCDUdevMonitorParser_GetNumProperties(&o->parser); +} + +void NCDUdevMonitor_GetProperty (NCDUdevMonitor *o, int index, const char **name, const char **value) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + NCDUdevMonitorParser_GetProperty(&o->parser, index, name, value); +} diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.h b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.h new file mode 100644 index 0000000..eae5747 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitor.h @@ -0,0 +1,71 @@ +/** + * @file NCDUdevMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMONITOR_H +#define BADVPN_UDEVMONITOR_NCDUDEVMONITOR_H + +#include +#include +#include +#include +#include + +#define NCDUDEVMONITOR_MODE_MONITOR_UDEV 0 +#define NCDUDEVMONITOR_MODE_INFO 1 +#define NCDUDEVMONITOR_MODE_MONITOR_KERNEL 2 + +typedef void (*NCDUdevMonitor_handler_event) (void *user); +typedef void (*NCDUdevMonitor_handler_error) (void *user, int is_error); + +typedef struct { + void *user; + NCDUdevMonitor_handler_event handler_event; + NCDUdevMonitor_handler_error handler_error; + BInputProcess process; + int process_running; + int process_was_error; + int input_running; + int input_was_error; + StreamRecvConnector connector; + NCDUdevMonitorParser parser; + DebugObject d_obj; + DebugError d_err; +} NCDUdevMonitor; + +int NCDUdevMonitor_Init (NCDUdevMonitor *o, BReactor *reactor, BProcessManager *manager, int mode, void *user, + NCDUdevMonitor_handler_event handler_event, + NCDUdevMonitor_handler_error handler_error) WARN_UNUSED; +void NCDUdevMonitor_Free (NCDUdevMonitor *o); +void NCDUdevMonitor_Done (NCDUdevMonitor *o); +void NCDUdevMonitor_AssertReady (NCDUdevMonitor *o); +int NCDUdevMonitor_IsReadyEvent (NCDUdevMonitor *o); +int NCDUdevMonitor_GetNumProperties (NCDUdevMonitor *o); +void NCDUdevMonitor_GetProperty (NCDUdevMonitor *o, int index, const char **name, const char **value); + +#endif diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.c b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.c new file mode 100644 index 0000000..9ec0ef1 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.c @@ -0,0 +1,358 @@ +/** + * @file NCDUdevMonitorParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#define PROPERTY_REGEX "^([^=]+)=(.*)$" + +static uint8_t * find_end (uint8_t *buf, size_t len) +{ + while (len >= 2) { + if (buf[0] == '\n' && buf[1] == '\n') { + return (buf + 2); + } + buf++; + len--; + } + + return NULL; +} + +static int parse_property (NCDUdevMonitorParser *o, char *data) +{ + ASSERT(o->ready_num_properties >= 0) + ASSERT(o->ready_num_properties <= o->max_properties) + + if (o->ready_num_properties == o->max_properties) { + BLog(BLOG_ERROR, "too many properties"); + return 0; + } + struct NCDUdevMonitorParser_property *prop = &o->ready_properties[o->ready_num_properties]; + + // execute property regex + regmatch_t matches[3]; + if (regexec(&o->property_regex, data, 3, matches, 0) != 0) { + BLog(BLOG_ERROR, "failed to parse property"); + return 0; + } + + // extract components + prop->name = data + matches[1].rm_so; + *(data + matches[1].rm_eo) = '\0'; + prop->value = data + matches[2].rm_so; + *(data + matches[2].rm_eo) = '\0'; + + // register property + o->ready_num_properties++; + + return 1; +} + +static int parse_message (NCDUdevMonitorParser *o) +{ + ASSERT(!o->is_ready) + ASSERT(o->ready_len >= 2) + ASSERT(o->buf[o->ready_len - 2] == '\n') + ASSERT(o->buf[o->ready_len - 1] == '\n') + + // zero terminate message (replacing the second newline) + o->buf[o->ready_len - 1] = '\0'; + + // start parsing + char *line = (char *)o->buf; + int first_line = 1; + + // set is not ready event + o->ready_is_ready_event = 0; + + // init properties + o->ready_num_properties = 0; + + do { + // find end of line + char *line_end = strchr(line, '\n'); + ASSERT(line_end) + + // zero terminate line + *line_end = '\0'; + + if (o->is_info_mode) { + // ignore W: entries with missing space + if (string_begins_with(line, "W:")) { + goto nextline; + } + + // parse prefix + if (strlen(line) < 3 || line[1] != ':' || line[2] != ' ') { + BLog(BLOG_ERROR, "failed to parse head"); + return 0; + } + char line_type = line[0]; + char *line_value = line + 3; + + if (first_line) { + if (line_type != 'P') { + BLog(BLOG_ERROR, "wrong first line type"); + return 0; + } + } else { + if (line_type == 'E') { + if (!parse_property(o, line_value)) { + return 0; + } + } + } + } else { + if (first_line) { + // is this the initial informational message? + if (string_begins_with(line, "monitor")) { + o->ready_is_ready_event = 1; + break; + } + + // check first line + if (!string_begins_with(line, "UDEV ") && !string_begins_with(line, "KERNEL")) { + BLog(BLOG_ERROR, "failed to parse head"); + return 0; + } + } else { + if (!parse_property(o, line)) { + return 0; + } + } + } + nextline: + + first_line = 0; + line = line_end + 1; + } while (*line); + + // set ready + o->is_ready = 1; + + return 1; +} + +static void process_data (NCDUdevMonitorParser *o) +{ + ASSERT(!o->is_ready) + + while (1) { + // look for end of event + uint8_t *c = find_end(o->buf, o->buf_used); + if (!c) { + // check for out of buffer condition + if (o->buf_used == o->buf_size) { + BLog(BLOG_ERROR, "out of buffer"); + o->buf_used = 0; + } + + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + return; + } + + // remember message length + o->ready_len = c - o->buf; + + // parse message + if (parse_message(o)) { + break; + } + + // shift buffer + memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len); + o->buf_used -= o->ready_len; + } + + // call handler + o->handler(o->user); + return; +} + +static void input_handler_done (NCDUdevMonitorParser *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->is_ready) + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - o->buf_used) + + // increment buffer position + o->buf_used += data_len; + + // process data + process_data(o); + return; +} + +static void done_job_handler (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + // shift buffer + memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len); + o->buf_used -= o->ready_len; + + // set not ready + o->is_ready = 0; + + // process data + process_data(o); + return; +} + +int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties, + int is_info_mode, BPendingGroup *pg, void *user, + NCDUdevMonitorParser_handler handler) +{ + ASSERT(buf_size > 0) + ASSERT(max_properties >= 0) + ASSERT(is_info_mode == 0 || is_info_mode == 1) + + // init arguments + o->input = input; + o->buf_size = buf_size; + o->max_properties = max_properties; + o->is_info_mode = is_info_mode; + o->user = user; + o->handler = handler; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // init property regex + if (regcomp(&o->property_regex, PROPERTY_REGEX, REG_EXTENDED) != 0) { + BLog(BLOG_ERROR, "regcomp failed"); + goto fail1; + } + + // init done job + BPending_Init(&o->done_job, pg, (BPending_handler)done_job_handler, o); + + // allocate buffer + if (!(o->buf = malloc(o->buf_size))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail2; + } + + // set buffer position + o->buf_used = 0; + + // set not ready + o->is_ready = 0; + + // allocate properties + if (!(o->ready_properties = BAllocArray(o->max_properties, sizeof(o->ready_properties[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail3; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + free(o->buf); +fail2: + BPending_Free(&o->done_job); + regfree(&o->property_regex); +fail1: + return 0; +} + +void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o) +{ + DebugObject_Free(&o->d_obj); + + // free properties + BFree(o->ready_properties); + + // free buffer + free(o->buf); + + // free done job + BPending_Free(&o->done_job); + + // free property regex + regfree(&o->property_regex); +} + +void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) +} + +void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + // schedule done job + BPending_Set(&o->done_job); +} + +int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + return o->ready_is_ready_event; +} + +int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + return o->ready_num_properties; +} + +void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + ASSERT(index >= 0) + ASSERT(index < o->ready_num_properties) + + *name = o->ready_properties[index].name; + *value = o->ready_properties[index].value; +} diff --git a/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.h b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.h new file mode 100644 index 0000000..910bbe0 --- /dev/null +++ b/app/src/main/jni/badvpn/udevmonitor/NCDUdevMonitorParser.h @@ -0,0 +1,76 @@ +/** + * @file NCDUdevMonitorParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMONITORPARSER_H +#define BADVPN_UDEVMONITOR_NCDUDEVMONITORPARSER_H + +#include +#include + +#include +#include +#include + +typedef void (*NCDUdevMonitorParser_handler) (void *user); + +struct NCDUdevMonitorParser_property { + char *name; + char *value; +}; + +typedef struct { + StreamRecvInterface *input; + int buf_size; + int max_properties; + int is_info_mode; + void *user; + NCDUdevMonitorParser_handler handler; + regex_t property_regex; + BPending done_job; + uint8_t *buf; + int buf_used; + int is_ready; + int ready_len; + int ready_is_ready_event; + struct NCDUdevMonitorParser_property *ready_properties; + int ready_num_properties; + DebugObject d_obj; +} NCDUdevMonitorParser; + +int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties, + int is_info_mode, BPendingGroup *pg, void *user, + NCDUdevMonitorParser_handler handler) WARN_UNUSED; +void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o); +int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o); +int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value); + +#endif diff --git a/app/src/main/jni/badvpn/udpgw/CMakeLists.txt b/app/src/main/jni/badvpn/udpgw/CMakeLists.txt new file mode 100644 index 0000000..c8c798c --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(badvpn-udpgw + udpgw.c +) +target_link_libraries(badvpn-udpgw system flow flowextra) + +install( + TARGETS badvpn-udpgw + RUNTIME DESTINATION bin +) diff --git a/app/src/main/jni/badvpn/udpgw/udpgw.c b/app/src/main/jni/badvpn/udpgw/udpgw.c new file mode 100644 index 0000000..9c6a341 --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw/udpgw.c @@ -0,0 +1,1473 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#include +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +#define DNS_UPDATE_TIME 2000 + +struct client { + BConnection con; + BAddr addr; + BTimer disconnect_timer; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + PacketPassFairQueue send_queue; + PacketStreamSender send_sender; + BAVL connections_tree; + LinkedList1 connections_list; + int num_connections; + LinkedList1 closing_connections_list; + LinkedList1Node clients_list_node; +}; + +struct connection { + struct client *client; + uint16_t conid; + BAddr addr; + BAddr orig_addr; + const uint8_t *first_data; + int first_data_len; + btime_t last_use_time; + int closing; + BPending first_job; + BufferWriter *send_if; + PacketProtoFlow send_ppflow; + PacketPassFairQueueFlow send_qflow; + union { + struct { + BDatagram udp_dgram; + int local_port_index; + BufferWriter udp_send_writer; + PacketBuffer udp_send_buffer; + SinglePacketBuffer udp_recv_buffer; + PacketPassInterface udp_recv_if; + BAVLNode connections_tree_node; + LinkedList1Node connections_list_node; + }; + struct { + LinkedList1Node closing_connections_list_node; + }; + }; +}; + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *listen_addrs[MAX_LISTEN_ADDRS]; + int num_listen_addrs; + int udp_mtu; + int max_clients; + int max_connections_for_client; + int client_socket_sndbuf; + int local_udp_num_ports; + char *local_udp_addr; + int local_udp_ip6_num_ports; + char *local_udp_ip6_addr; + int unique_local_ports; +} options; + +// MTUs +int udpgw_mtu; +int pp_mtu; + +// listen addresses +BAddr listen_addrs[MAX_LISTEN_ADDRS]; +int num_listen_addrs; + +// local UDP port range, if options.local_udp_num_ports>=0 +BAddr local_udp_addr; + +// local UDP/IPv6 port range, if options.local_udp_ip6_num_ports>=0 +BAddr local_udp_ip6_addr; + +// DNS forwarding +BAddr dns_addr; +btime_t last_dns_update_time; + +// reactor +BReactor ss; + +// listeners +BListener listeners[MAX_LISTEN_ADDRS]; +int num_listeners; + +// clients +LinkedList1 clients_list; +int num_clients; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static void listener_handler (BListener *listener); +static void client_free (struct client *client); +static void client_logfunc (struct client *client); +static void client_log (struct client *client, int level, const char *fmt, ...); +static void client_disconnect_timer_handler (struct client *client); +static void client_connection_handler (struct client *client, int event); +static void client_decoder_handler_error (struct client *client); +static void client_recv_if_handler_send (struct client *client, uint8_t *data, int data_len); +static int get_local_num_ports (int addr_type); +static BAddr get_local_addr (int addr_type); +static uint8_t * build_port_usage_array_and_find_least_used_connection (BAddr remote_addr, struct connection **out_con); +static void connection_init (struct client *client, uint16_t conid, BAddr addr, BAddr orig_addr, const uint8_t *data, int data_len); +static void connection_free (struct connection *con); +static void connection_logfunc (struct connection *con); +static void connection_log (struct connection *con, int level, const char *fmt, ...); +static void connection_free_udp (struct connection *con); +static void connection_first_job_handler (struct connection *con); +static void connection_send_to_client (struct connection *con, uint8_t flags, const uint8_t *data, int data_len); +static int connection_send_to_udp (struct connection *con, const uint8_t *data, int data_len); +static void connection_close (struct connection *con); +static void connection_send_qflow_busy_handler (struct connection *con); +static void connection_dgram_handler_event (struct connection *con, int event); +static void connection_udp_recv_if_handler_send (struct connection *con, uint8_t *data, int data_len); +static struct connection * find_connection (struct client *client, uint16_t conid); +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); +static void maybe_update_dns (void); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // compute MTUs + udpgw_mtu = udpgw_compute_mtu(options.udp_mtu); + if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { + udpgw_mtu = PACKETPROTO_MAXPAYLOAD; + } + pp_mtu = udpgw_mtu + sizeof(struct packetproto_header); + + // init time + BTime_Init(); + + // init DNS forwarding + BAddr_InitNone(&dns_addr); + last_dns_update_time = INT64_MIN; + maybe_update_dns(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // initialize listeners + num_listeners = 0; + while (num_listeners < num_listen_addrs) { + if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail3; + } + num_listeners++; + } + + // init clients list + LinkedList1_Init(&clients_list); + num_clients = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + while (!LinkedList1_IsEmpty(&clients_list)) { + struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node); + client_free(client); + } +fail3: + // free listeners + while (num_listeners > 0) { + num_listeners--; + BListener_Free(&listeners[num_listeners]); + } + // finish signal handling + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&ss); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--listen-addr ] ...\n" + " [--udp-mtu ]\n" + " [--max-clients ]\n" + " [--max-connections-for-client ]\n" + " [--client-socket-sndbuf ]\n" + " [--local-udp-addrs ]\n" + " [--local-udp-ip6-addrs ]\n" + " [--unique-local-ports]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.num_listen_addrs = 0; + options.udp_mtu = DEFAULT_UDP_MTU; + options.max_clients = DEFAULT_MAX_CLIENTS; + options.max_connections_for_client = DEFAULT_MAX_CONNECTIONS_FOR_CLIENT; + options.client_socket_sndbuf = CLIENT_DEFAULT_SOCKET_SEND_BUFFER; + options.local_udp_num_ports = -1; + options.local_udp_ip6_num_ports = -1; + options.unique_local_ports = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_listen_addrs == MAX_LISTEN_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.listen_addrs[options.num_listen_addrs] = argv[i + 1]; + options.num_listen_addrs++; + i++; + } + else if (!strcmp(arg, "--udp-mtu")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udp_mtu = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-connections-for-client")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connections_for_client = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--client-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.client_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--local-udp-addrs")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + options.local_udp_addr = argv[i + 1]; + if ((options.local_udp_num_ports = atoi(argv[i + 2])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i += 2; + } + else if (!strcmp(arg, "--local-udp-ip6-addrs")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + options.local_udp_ip6_addr = argv[i + 1]; + if ((options.local_udp_ip6_num_ports = atoi(argv[i + 2])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i += 2; + } + else if (!strcmp(arg, "--unique-local-ports")) { + options.unique_local_ports = 1; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen addresses + num_listen_addrs = 0; + while (num_listen_addrs < options.num_listen_addrs) { + if (!BAddr_Parse(&listen_addrs[num_listen_addrs], options.listen_addrs[num_listen_addrs], NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + num_listen_addrs++; + } + + // resolve local UDP address + if (options.local_udp_num_ports >= 0) { + if (!BAddr_Parse(&local_udp_addr, options.local_udp_addr, NULL, 0)) { + BLog(BLOG_ERROR, "local udp addr: BAddr_Parse failed"); + return 0; + } + if (local_udp_addr.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "local udp addr: must be an IPv4 address"); + return 0; + } + } + + // resolve local UDP/IPv6 address + if (options.local_udp_ip6_num_ports >= 0) { + if (!BAddr_Parse(&local_udp_ip6_addr, options.local_udp_ip6_addr, NULL, 0)) { + BLog(BLOG_ERROR, "local udp ip6 addr: BAddr_Parse failed"); + return 0; + } + if (local_udp_ip6_addr.type != BADDR_TYPE_IPV6) { + BLog(BLOG_ERROR, "local udp ip6 addr: must be an IPv6 address"); + return 0; + } + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void listener_handler (BListener *listener) +{ + if (num_clients == options.max_clients) { + BLog(BLOG_ERROR, "maximum number of clients reached"); + goto fail0; + } + + // allocate structure + struct client *client = (struct client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // accept client + if (!BConnection_Init(&client->con, BConnection_source_listener(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // limit socket send buffer, else our scheduling is pointless + if (options.client_socket_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&client->con, options.client_socket_sndbuf)) { + BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + // init connection interfaces + BConnection_SendAsync_Init(&client->con); + BConnection_RecvAsync_Init(&client->con); + + // init disconnect timer + BTimer_Init(&client->disconnect_timer, CLIENT_DISCONNECT_TIMEOUT, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // init recv interface + PacketPassInterface_Init(&client->recv_if, udpgw_mtu, (PacketPassInterface_handler_send)client_recv_if_handler_send, client, BReactor_PendingGroup(&ss)); + + // init recv decoder + if (!PacketProtoDecoder_Init(&client->recv_decoder, BConnection_RecvAsync_GetIf(&client->con), &client->recv_if, BReactor_PendingGroup(&ss), client, + (PacketProtoDecoder_handler_error)client_decoder_handler_error + )) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + // init send sender + PacketStreamSender_Init(&client->send_sender, BConnection_SendAsync_GetIf(&client->con), pp_mtu, BReactor_PendingGroup(&ss)); + + // init send queue + if (!PacketPassFairQueue_Init(&client->send_queue, PacketStreamSender_GetInput(&client->send_sender), BReactor_PendingGroup(&ss), 0, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail3; + } + + // init connections tree + BAVL_Init(&client->connections_tree, OFFSET_DIFF(struct connection, conid, connections_tree_node), (BAVL_comparator)uint16_comparator, NULL); + + // init connections list + LinkedList1_Init(&client->connections_list); + + // set zero connections + client->num_connections = 0; + + // init closing connections list + LinkedList1_Init(&client->closing_connections_list); + + // insert to clients list + LinkedList1_Append(&clients_list, &client->clients_list_node); + num_clients++; + + client_log(client, BLOG_INFO, "connected"); + + return; + +fail3: + PacketStreamSender_Free(&client->send_sender); + PacketProtoDecoder_Free(&client->recv_decoder); +fail2: + PacketPassInterface_Free(&client->recv_if); + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_free (struct client *client) +{ + // allow freeing send queue flows + PacketPassFairQueue_PrepareFree(&client->send_queue); + + // free connections + while (!LinkedList1_IsEmpty(&client->connections_list)) { + struct connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&client->connections_list), struct connection, connections_list_node); + connection_free(con); + } + + // free closing connections + while (!LinkedList1_IsEmpty(&client->closing_connections_list)) { + struct connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&client->closing_connections_list), struct connection, closing_connections_list_node); + connection_free(con); + } + + // remove from clients list + LinkedList1_Remove(&clients_list, &client->clients_list_node); + num_clients--; + + // free send queue + PacketPassFairQueue_Free(&client->send_queue); + + // free send sender + PacketStreamSender_Free(&client->send_sender); + + // free recv decoder + PacketProtoDecoder_Free(&client->recv_decoder); + + // free recv interface + PacketPassInterface_Free(&client->recv_if); + + // free disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free connection interfaces + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free structure + free(client); +} + +void client_logfunc (struct client *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client (%s): ", addr); +} + +void client_log (struct client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client *client) +{ + client_log(client, BLOG_INFO, "timed out, disconnecting"); + + // free client + client_free(client); +} + +void client_connection_handler (struct client *client, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "client closed"); + } else { + client_log(client, BLOG_INFO, "client error"); + } + + // free client + client_free(client); +} + +void client_decoder_handler_error (struct client *client) +{ + client_log(client, BLOG_ERROR, "decoder error"); + + // free client + client_free(client); +} + +void client_recv_if_handler_send (struct client *client, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&client->recv_if); + + // parse header + if (data_len < sizeof(struct udpgw_header)) { + client_log(client, BLOG_ERROR, "missing header"); + return; + } + struct udpgw_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + uint16_t conid = ltoh16(header.conid); + + // reset disconnect timer + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // if this is keepalive, ignore any payload + if ((flags & UDPGW_CLIENT_FLAG_KEEPALIVE)) { + client_log(client, BLOG_DEBUG, "received keepalive"); + return; + } + + // parse address + BAddr orig_addr; + if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + client_log(client, BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&orig_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + client_log(client, BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&orig_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check payload length + if (data_len > options.udp_mtu) { + client_log(client, BLOG_ERROR, "too much data"); + return; + } + + // find connection + struct connection *con = find_connection(client, conid); + ASSERT(!con || !con->closing) + + // if connection exists, close it if needed + if (con && ((flags & UDPGW_CLIENT_FLAG_REBIND) || !BAddr_Compare(&con->orig_addr, &orig_addr))) { + connection_log(con, BLOG_DEBUG, "close old"); + connection_close(con); + con = NULL; + } + + // if connection doesn't exists, create it + if (!con) { + // check number of connections + if (client->num_connections == options.max_connections_for_client) { + // close least recently used connection + con = UPPER_OBJECT(LinkedList1_GetFirst(&client->connections_list), struct connection, connections_list_node); + connection_close(con); + } + + // if this is DNS, replace actual address, but keep still remember the orig_addr + BAddr addr = orig_addr; + if ((flags & UDPGW_CLIENT_FLAG_DNS)) { + maybe_update_dns(); + if (dns_addr.type == BADDR_TYPE_NONE) { + client_log(client, BLOG_WARNING, "received DNS packet, but no DNS server available"); + } else { + client_log(client, BLOG_DEBUG, "received DNS"); + addr = dns_addr; + } + } + + // create new connection + connection_init(client, conid, addr, orig_addr, data, data_len); + } else { + // submit packet to existing connection + connection_send_to_udp(con, data, data_len); + } +} + +int get_local_num_ports (int addr_type) +{ + switch (addr_type) { + case BADDR_TYPE_IPV4: return options.local_udp_num_ports; + case BADDR_TYPE_IPV6: return options.local_udp_ip6_num_ports; + default: ASSERT(0); return 0; + } +} + +BAddr get_local_addr (int addr_type) +{ + ASSERT(get_local_num_ports(addr_type) >= 0) + + switch (addr_type) { + case BADDR_TYPE_IPV4: return local_udp_addr; + case BADDR_TYPE_IPV6: return local_udp_ip6_addr; + default: ASSERT(0); return BAddr_MakeNone(); + } +} + +uint8_t * build_port_usage_array_and_find_least_used_connection (BAddr remote_addr, struct connection **out_con) +{ + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(get_local_num_ports(remote_addr.type) >= 0) + + int local_num_ports = get_local_num_ports(remote_addr.type); + + // allocate port usage array + uint8_t *port_usage = (uint8_t *)BAllocSize(bsize_fromint(local_num_ports)); + if (!port_usage) { + return NULL; + } + + // zero array + memset(port_usage, 0, local_num_ports); + + struct connection *least_con = NULL; + + // flag inappropriate ports (those with the same remote address) + for (LinkedList1Node *ln = LinkedList1_GetFirst(&clients_list); ln; ln = LinkedList1Node_Next(ln)) { + struct client *client = UPPER_OBJECT(ln, struct client, clients_list_node); + + for (LinkedList1Node *ln2 = LinkedList1_GetFirst(&client->connections_list); ln2; ln2 = LinkedList1Node_Next(ln2)) { + struct connection *con = UPPER_OBJECT(ln2, struct connection, connections_list_node); + ASSERT(con->client == client) + ASSERT(!con->closing) + + if (con->addr.type != remote_addr.type || con->local_port_index < 0) { + continue; + } + ASSERT(con->local_port_index < local_num_ports) + + if (options.unique_local_ports) { + BIPAddr ip1; + BIPAddr ip2; + BAddr_GetIPAddr(&con->addr, &ip1); + BAddr_GetIPAddr(&remote_addr, &ip2); + if (!BIPAddr_Compare(&ip1, &ip2)) { + continue; + } + } else { + if (!BAddr_Compare(&con->addr, &remote_addr)) { + continue; + } + } + + port_usage[con->local_port_index] = 1; + + if (!PacketPassFairQueueFlow_IsBusy(&con->send_qflow)) { + if (!least_con || con->last_use_time < least_con->last_use_time) { + least_con = con; + } + } + } + } + + *out_con = least_con; + return port_usage; +} + +void connection_init (struct client *client, uint16_t conid, BAddr addr, BAddr orig_addr, const uint8_t *data, int data_len) +{ + ASSERT(client->num_connections < options.max_connections_for_client) + ASSERT(!find_connection(client, conid)) + BAddr_Assert(&addr); + ASSERT(addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6) + ASSERT(orig_addr.type == BADDR_TYPE_IPV4 || orig_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + // allocate structure + struct connection *con = (struct connection *)malloc(sizeof(*con)); + if (!con) { + client_log(client, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = client; + con->conid = conid; + con->addr = addr; + con->orig_addr = orig_addr; + con->first_data = data; + con->first_data_len = data_len; + + // set last use time + con->last_use_time = btime_gettime(); + + // set not closing + con->closing = 0; + + // init first job + BPending_Init(&con->first_job, BReactor_PendingGroup(&ss), (BPending_handler)connection_first_job_handler, con); + BPending_Set(&con->first_job); + + // init send queue flow + PacketPassFairQueueFlow_Init(&con->send_qflow, &client->send_queue); + + // init send PacketProtoFlow + if (!PacketProtoFlow_Init(&con->send_ppflow, udpgw_mtu, CONNECTION_CLIENT_BUFFER_SIZE, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); + + // init UDP dgram + if (!BDatagram_Init(&con->udp_dgram, addr.type, &ss, con, (BDatagram_handler)connection_dgram_handler_event)) { + client_log(client, BLOG_ERROR, "BDatagram_Init failed"); + goto fail2; + } + + con->local_port_index = -1; + + int local_num_ports = get_local_num_ports(addr.type); + + if (local_num_ports >= 0) { + // build port usage array, find least used connection + struct connection *least_con; + uint8_t *port_usage = build_port_usage_array_and_find_least_used_connection(addr, &least_con); + if (!port_usage) { + client_log(client, BLOG_ERROR, "build_port_usage_array failed"); + goto failed; + } + + // set SO_REUSEADDR + if (!BDatagram_SetReuseAddr(&con->udp_dgram, 1)) { + client_log(client, BLOG_ERROR, "set SO_REUSEADDR failed"); + goto failed; + } + + // get starting local address + BAddr local_addr = get_local_addr(addr.type); + + // try different ports + for (int i = 0; i < local_num_ports; i++) { + // skip inappropriate ports + if (port_usage[i]) { + continue; + } + + BAddr bind_addr = local_addr; + BAddr_SetPort(&bind_addr, hton16(ntoh16(BAddr_GetPort(&bind_addr)) + (uint16_t)i)); + if (BDatagram_Bind(&con->udp_dgram, bind_addr)) { + // remember which port we're using + con->local_port_index = i; + goto cont; + } + } + + // try closing an unused connection with the same remote addr + if (!least_con) { + goto failed; + } + + ASSERT(least_con->addr.type == addr.type) + ASSERT(least_con->local_port_index >= 0) + ASSERT(least_con->local_port_index < local_num_ports) + ASSERT(!PacketPassFairQueueFlow_IsBusy(&least_con->send_qflow)) + + int i = least_con->local_port_index; + + BLog(BLOG_INFO, "closing connection for its remote address"); + + // close the offending connection + connection_close(least_con); + + // try binding to its port + BAddr bind_addr = local_addr; + BAddr_SetPort(&bind_addr, hton16(ntoh16(BAddr_GetPort(&bind_addr)) + (uint16_t)i)); + if (BDatagram_Bind(&con->udp_dgram, bind_addr)) { + // remember which port we're using + con->local_port_index = i; + goto cont; + } + + failed: + client_log(client, BLOG_WARNING, "failed to bind to any local address; proceeding regardless"); + cont:; + BFree(port_usage); + } + + // set UDP dgram send address + BIPAddr ipaddr; + BIPAddr_InitInvalid(&ipaddr); + BDatagram_SetSendAddrs(&con->udp_dgram, addr, ipaddr); + + // init UDP dgram interfaces + BDatagram_SendAsync_Init(&con->udp_dgram, options.udp_mtu); + BDatagram_RecvAsync_Init(&con->udp_dgram, options.udp_mtu); + + // init UDP writer + BufferWriter_Init(&con->udp_send_writer, options.udp_mtu, BReactor_PendingGroup(&ss)); + + // init UDP buffer + if (!PacketBuffer_Init(&con->udp_send_buffer, BufferWriter_GetOutput(&con->udp_send_writer), BDatagram_SendAsync_GetIf(&con->udp_dgram), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail4; + } + + // init UDP recv interface + PacketPassInterface_Init(&con->udp_recv_if, options.udp_mtu, (PacketPassInterface_handler_send)connection_udp_recv_if_handler_send, con, BReactor_PendingGroup(&ss)); + + // init UDP recv buffer + if (!SinglePacketBuffer_Init(&con->udp_recv_buffer, BDatagram_RecvAsync_GetIf(&con->udp_dgram), &con->udp_recv_if, BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail5; + } + + // insert to client's connections tree + ASSERT_EXECUTE(BAVL_Insert(&client->connections_tree, &con->connections_tree_node, NULL)) + + // insert to client's connections list + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // increment number of connections + client->num_connections++; + + connection_log(con, BLOG_DEBUG, "initialized"); + + return; + +fail5: + PacketPassInterface_Free(&con->udp_recv_if); + PacketBuffer_Free(&con->udp_send_buffer); +fail4: + BufferWriter_Free(&con->udp_send_writer); + BDatagram_RecvAsync_Free(&con->udp_dgram); + BDatagram_SendAsync_Free(&con->udp_dgram); + BDatagram_Free(&con->udp_dgram); +fail2: + PacketProtoFlow_Free(&con->send_ppflow); +fail1: + PacketPassFairQueueFlow_Free(&con->send_qflow); + BPending_Free(&con->first_job); + free(con); +fail0: + return; +} + +void connection_free (struct connection *con) +{ + struct client *client = con->client; + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + if (con->closing) { + // remove from client's closing connections list + LinkedList1_Remove(&client->closing_connections_list, &con->closing_connections_list_node); + } else { + // decrement number of connections + client->num_connections--; + + // remove from client's connections list + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + + // remove from client's connections tree + BAVL_Remove(&client->connections_tree, &con->connections_tree_node); + + // free UDP + connection_free_udp(con); + } + + // free send PacketProtoFlow + PacketProtoFlow_Free(&con->send_ppflow); + + // free send queue flow + PacketPassFairQueueFlow_Free(&con->send_qflow); + + // free first job + BPending_Free(&con->first_job); + + // free structure + free(con); +} + +void connection_logfunc (struct connection *con) +{ + client_logfunc(con->client); + + if (con->closing) { + BLog_Append("old connection %"PRIu16": ", con->conid); + } else { + BLog_Append("connection %"PRIu16": ", con->conid); + } +} + +void connection_log (struct connection *con, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)connection_logfunc, con, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void connection_free_udp (struct connection *con) +{ + // free UDP receive buffer + SinglePacketBuffer_Free(&con->udp_recv_buffer); + + // free UDP receive interface + PacketPassInterface_Free(&con->udp_recv_if); + + // free UDP buffer + PacketBuffer_Free(&con->udp_send_buffer); + + // free UDP writer + BufferWriter_Free(&con->udp_send_writer); + + // free UDP dgram interfaces + BDatagram_RecvAsync_Free(&con->udp_dgram); + BDatagram_SendAsync_Free(&con->udp_dgram); + + // free UDP dgram + BDatagram_Free(&con->udp_dgram); +} + +void connection_first_job_handler (struct connection *con) +{ + ASSERT(!con->closing) + + connection_send_to_udp(con, con->first_data, con->first_data_len); +} + +void connection_send_to_client (struct connection *con, uint8_t flags, const uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + size_t addr_len = (con->orig_addr.type == BADDR_TYPE_IPV6) ? sizeof(struct udpgw_addr_ipv6) : + (con->orig_addr.type == BADDR_TYPE_IPV4) ? sizeof(struct udpgw_addr_ipv4) : 0; + if (data_len > udpgw_mtu - (int)(sizeof(struct udpgw_header) + addr_len)) { + connection_log(con, BLOG_WARNING, "packet is too large, cannot send to client"); + return; + } + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(con->send_if, &out)) { + connection_log(con, BLOG_ERROR, "out of client buffer"); + return; + } + int out_pos = 0; + + if (con->orig_addr.type == BADDR_TYPE_IPV6) { + flags |= UDPGW_CLIENT_FLAG_IPV6; + } + + // write header + struct udpgw_header header; + header.flags = htol8(flags); + header.conid = htol16(con->conid); + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (con->orig_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = con->orig_addr.ipv4.ip; + addr_ipv4.addr_port = con->orig_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, con->orig_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = con->orig_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write message + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit written message + ASSERT(out_pos <= udpgw_mtu) + BufferWriter_EndPacket(con->send_if, out_pos); +} + +int connection_send_to_udp (struct connection *con, const uint8_t *data, int data_len) +{ + struct client *client = con->client; + ASSERT(!con->closing) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + connection_log(con, BLOG_DEBUG, "from client %d bytes", data_len); + + // set last use time + con->last_use_time = btime_gettime(); + + // move connection to front + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(&con->udp_send_writer, &out)) { + connection_log(con, BLOG_ERROR, "out of UDP buffer"); + return 0; + } + + // write message + memcpy(out, data, data_len); + + // submit written message + BufferWriter_EndPacket(&con->udp_send_writer, data_len); + + return 1; +} + +void connection_close (struct connection *con) +{ + struct client *client = con->client; + ASSERT(!con->closing) + + // if possible, free connection immediately + if (!PacketPassFairQueueFlow_IsBusy(&con->send_qflow)) { + connection_free(con); + return; + } + + connection_log(con, BLOG_DEBUG, "closing later"); + + // decrement number of connections + client->num_connections--; + + // remove from client's connections list + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + + // remove from client's connections tree + BAVL_Remove(&client->connections_tree, &con->connections_tree_node); + + // free UDP + connection_free_udp(con); + + // insert to client's closing connections list + LinkedList1_Append(&client->closing_connections_list, &con->closing_connections_list_node); + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&con->send_qflow, (PacketPassFairQueue_handler_busy)connection_send_qflow_busy_handler, con); + + // unset first job + BPending_Unset(&con->first_job); + + // set closing + con->closing = 1; +} + +void connection_send_qflow_busy_handler (struct connection *con) +{ + ASSERT(con->closing) + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + connection_log(con, BLOG_DEBUG, "closing finally"); + + // free connection + connection_free(con); +} + +void connection_dgram_handler_event (struct connection *con, int event) +{ + ASSERT(!con->closing) + + connection_log(con, BLOG_INFO, "UDP error"); + + // close connection + connection_close(con); +} + +void connection_udp_recv_if_handler_send (struct connection *con, uint8_t *data, int data_len) +{ + struct client *client = con->client; + ASSERT(!con->closing) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + connection_log(con, BLOG_DEBUG, "from UDP %d bytes", data_len); + + // set last use time + con->last_use_time = btime_gettime(); + + // move connection to front + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // accept packet + PacketPassInterface_Done(&con->udp_recv_if); + + // send packet to client + connection_send_to_client(con, 0, data, data_len); +} + +struct connection * find_connection (struct client *client, uint16_t conid) +{ + BAVLNode *tree_node = BAVL_LookupExact(&client->connections_tree, &conid); + if (!tree_node) { + return NULL; + } + struct connection *con = UPPER_OBJECT(tree_node, struct connection, connections_tree_node); + ASSERT(con->conid == conid) + ASSERT(!con->closing) + + return con; +} + +int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + return B_COMPARE(*v1, *v2); +} + +void maybe_update_dns (void) +{ +#ifndef BADVPN_USE_WINAPI + btime_t now = btime_gettime(); + if (now < btime_add(last_dns_update_time, DNS_UPDATE_TIME)) { + return; + } + last_dns_update_time = now; + BLog(BLOG_DEBUG, "update dns"); + + if (res_init() != 0) { + BLog(BLOG_ERROR, "res_init failed"); + goto fail; + } + + if (_res.nscount == 0) { + BLog(BLOG_ERROR, "no name servers available"); + goto fail; + } + + BAddr addr; + BAddr_InitIPv4(&addr, _res.nsaddr_list[0].sin_addr.s_addr, hton16(53)); + + if (!BAddr_Compare(&addr, &dns_addr)) { + char str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&addr, str); + BLog(BLOG_INFO, "using DNS server %s", str); + } + + dns_addr = addr; + return; + +fail: + BAddr_InitNone(&dns_addr); +#endif +} diff --git a/app/src/main/jni/badvpn/udpgw/udpgw.h b/app/src/main/jni/badvpn/udpgw/udpgw.h new file mode 100644 index 0000000..f63f857 --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw/udpgw.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) Ambroz Bizjak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "udpgw" + +// maxiumum listen addresses +#define MAX_LISTEN_ADDRS 16 + +// maximum datagram size +#define DEFAULT_UDP_MTU 65520 + +// connection buffer size for sending to client, in packets +#define CONNECTION_CLIENT_BUFFER_SIZE 1 + +// connection buffer size for sending to UDP, in packets +#define CONNECTION_UDP_BUFFER_SIZE 1 + +// maximum number of clients +#define DEFAULT_MAX_CLIENTS 3 + +// maximum connections for client +#define DEFAULT_MAX_CONNECTIONS_FOR_CLIENT 256 + +// how long after nothing has been received to disconnect a client +#define CLIENT_DISCONNECT_TIMEOUT 20000 + +// SO_SNDBFUF socket option for clients, 0 to not set +#define CLIENT_DEFAULT_SOCKET_SEND_BUFFER 1048576 diff --git a/app/src/main/jni/badvpn/udpgw_client/CMakeLists.txt b/app/src/main/jni/badvpn/udpgw_client/CMakeLists.txt new file mode 100644 index 0000000..7908820 --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw_client/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(udpgw_client UdpGwClient.c) +target_link_libraries(udpgw_client system flow flowextra) diff --git a/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.c b/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.c new file mode 100644 index 0000000..85d069a --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2); +static void free_server (UdpGwClient *o); +static void decoder_handler_error (UdpGwClient *o); +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len); +static void send_monitor_handler (UdpGwClient *o); +static void keepalive_if_handler_done (UdpGwClient *o); +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid); +static uint16_t find_unused_conid (UdpGwClient *o); +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len); +static void connection_free (struct UdpGwClient_connection *con); +static void connection_first_job_handler (struct UdpGwClient_connection *con); +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len); +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + return B_COMPARE(*v1, *v2); +} + +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2) +{ + int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); + if (r) { + return r; + } + return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); +} + +static void free_server (UdpGwClient *o) +{ + // disconnect send connector + PacketPassConnector_DisconnectOutput(&o->send_connector); + + // free send sender + PacketStreamSender_Free(&o->send_sender); + + // free receive decoder + PacketProtoDecoder_Free(&o->recv_decoder); + + // free receive interface + PacketPassInterface_Free(&o->recv_if); +} + +static void decoder_handler_error (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + BLog(BLOG_ERROR, "decoder error"); + + // report error + o->handler_servererror(o->user); + return; +} + +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&o->recv_if); + + // check header + if (data_len < sizeof(struct udpgw_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct udpgw_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + uint16_t conid = ltoh16(header.conid); + + // parse address + BAddr remote_addr; + if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + BLog(BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + BLog(BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check remaining data + if (data_len > o->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // find connection + struct UdpGwClient_connection *con = find_connection_by_conid(o, conid); + if (!con) { + BLog(BLOG_ERROR, "unknown conid"); + return; + } + + // check remote address + if (BAddr_CompareOrder(&con->conaddr.remote_addr, &remote_addr) != 0) { + BLog(BLOG_ERROR, "wrong remote address"); + return; + } + + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // pass packet to user + o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len); + return; +} + +static void send_monitor_handler (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + + if (o->keepalive_sending) { + return; + } + + BLog(BLOG_INFO, "keepalive"); + + // send keepalive + PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet)); + + // set sending keep-alive + o->keepalive_sending = 1; +} + +static void keepalive_if_handler_done (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->keepalive_sending) + + // set not sending keepalive + o->keepalive_sending = 0; +} + +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node); +} + +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node); +} + +static uint16_t find_unused_conid (UdpGwClient *o) +{ + ASSERT(o->num_connections < o->max_connections) + + while (1) { + if (!find_connection_by_conid(o, o->next_conid)) { + return o->next_conid; + } + + if (o->next_conid == o->max_connections - 1) { + o->next_conid = 0; + } else { + o->next_conid++; + } + } +} + +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len) +{ + ASSERT(o->num_connections < o->max_connections) + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // allocate structure + struct UdpGwClient_connection *con = (struct UdpGwClient_connection *)malloc(sizeof(*con)); + if (!con) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = o; + con->conaddr = conaddr; + con->first_flags = flags; + con->first_data = data; + con->first_data_len = data_len; + + // allocate conid + con->conid = find_unused_conid(o); + + // init first job + BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con); + BPending_Set(&con->first_job); + + // init queue flow + PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + // insert to connections tree by conid + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL)) + + // insert to connections list + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // increment number of connections + o->num_connections++; + + return; + +fail1: + PacketPassFairQueueFlow_Free(&con->send_qflow); + BPending_Free(&con->first_job); + free(con); +fail0: + return; +} + +static void connection_free (struct UdpGwClient_connection *con) +{ + UdpGwClient *o = con->client; + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + // decrement number of connections + o->num_connections--; + + // remove from connections list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + + // remove from connections tree by conid + BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // free PacketProtoFlow + PacketProtoFlow_Free(&con->send_ppflow); + + // free queue flow + PacketPassFairQueueFlow_Free(&con->send_qflow); + + // free first job + BPending_Free(&con->first_job); + + // free structure + free(con); +} + +static void connection_first_job_handler (struct UdpGwClient_connection *con) +{ + connection_send(con, UDPGW_CLIENT_FLAG_REBIND|con->first_flags, con->first_data, con->first_data_len); +} + +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len) +{ + UdpGwClient *o = con->client; + B_USE(o) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(con->send_if, &out)) { + BLog(BLOG_ERROR, "out of buffer"); + return; + } + int out_pos = 0; + + if (con->conaddr.remote_addr.type == BADDR_TYPE_IPV6) { + flags |= UDPGW_CLIENT_FLAG_IPV6; + } + + // write header + struct udpgw_header header; + header.flags = ltoh8(flags); + header.conid = ltoh16(con->conid); + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (con->conaddr.remote_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = con->conaddr.remote_addr.ipv4.ip; + addr_ipv4.addr_port = con->conaddr.remote_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, con->conaddr.remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = con->conaddr.remote_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write packet to buffer + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit packet to buffer + BufferWriter_EndPacket(con->send_if, out_pos); +} + +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(o->num_connections > 0) + + // get least recently used connection + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // set new conaddr + con->conaddr = conaddr; + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + return con; +} + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) +{ + ASSERT(udp_mtu >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD) + ASSERT(max_connections > 0) + ASSERT(send_buffer_size > 0) + + // init arguments + o->udp_mtu = udp_mtu; + o->max_connections = max_connections; + o->send_buffer_size = send_buffer_size; + o->keepalive_time = keepalive_time; + o->reactor = reactor; + o->user = user; + o->handler_servererror = handler_servererror; + o->handler_received = handler_received; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // compute MTUs + o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); + o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header); + + // init connections tree by conaddr + BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL); + + // init connections tree by conid + BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL); + + // init connections list + LinkedList1_Init(&o->connections_list); + + // set zero connections + o->num_connections = 0; + + // set next conid + o->next_conid = 0; + + // init send connector + PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // init send monitor + PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o); + + // init send queue + if (!PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1)) { + goto fail0; + } + + // construct keepalive packet + o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw); + memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw)); + o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE; + + // init keepalive queue flow + PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue); + o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow); + + // init keepalive output + PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o); + + // set not sending keepalive + o->keepalive_sending = 0; + + // set have no server + o->have_server = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + PacketPassInactivityMonitor_Free(&o->send_monitor); + PacketPassConnector_Free(&o->send_connector); + return 0; +} + +void UdpGwClient_Free (UdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + + // allow freeing send queue flows + PacketPassFairQueue_PrepareFree(&o->send_queue); + + // free connections + while (!LinkedList1_IsEmpty(&o->connections_list)) { + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + connection_free(con); + } + + // free server + if (o->have_server) { + free_server(o); + } + + // free keepalive queue flow + PacketPassFairQueueFlow_Free(&o->keepalive_qflow); + + // free send queue + PacketPassFairQueue_Free(&o->send_queue); + + // free send + PacketPassInactivityMonitor_Free(&o->send_monitor); + + // free send connector + PacketPassConnector_Free(&o->send_connector); +} + +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // build conaddr + struct UdpGwClient_conaddr conaddr; + conaddr.local_addr = local_addr; + conaddr.remote_addr = remote_addr; + + // lookup connection + struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr); + + uint8_t flags = 0; + + if (is_dns) { + // route to remote DNS server instead of provided address + flags |= UDPGW_CLIENT_FLAG_DNS; + } + + // if no connection and can't create a new one, reuse the least recently used une + if (!con && o->num_connections == o->max_connections) { + con = reuse_connection(o, conaddr); + flags |= UDPGW_CLIENT_FLAG_REBIND; + } + + if (!con) { + // create new connection + connection_init(o, conaddr, flags, data, data_len); + } else { + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // send packet to existing connection + connection_send(con, flags, data, data_len); + } +} + +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_server) + + // init receive interface + PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->recv_decoder, recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init send sender + PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // connect send connector + PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender)); + + // set have server + o->have_server = 1; + + return 1; + +fail1: + PacketPassInterface_Free(&o->recv_if); + return 0; +} + +void UdpGwClient_DisconnectServer (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + // free server + free_server(o); + + // set have no server + o->have_server = 0; +} diff --git a/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.h b/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.h new file mode 100644 index 0000000..0a1086b --- /dev/null +++ b/app/src/main/jni/badvpn/udpgw_client/UdpGwClient.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H +#define BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*UdpGwClient_handler_servererror) (void *user); +typedef void (*UdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +B_START_PACKED +struct UdpGwClient__keepalive_packet { + struct packetproto_header pp; + struct udpgw_header udpgw; +} B_PACKED; +B_END_PACKED + +typedef struct { + int udp_mtu; + int max_connections; + int send_buffer_size; + btime_t keepalive_time; + BReactor *reactor; + void *user; + UdpGwClient_handler_servererror handler_servererror; + UdpGwClient_handler_received handler_received; + int udpgw_mtu; + int pp_mtu; + BAVL connections_tree_by_conaddr; + BAVL connections_tree_by_conid; + LinkedList1 connections_list; + int num_connections; + int next_conid; + PacketPassFairQueue send_queue; + PacketPassInactivityMonitor send_monitor; + PacketPassConnector send_connector; + struct UdpGwClient__keepalive_packet keepalive_packet; + PacketPassInterface *keepalive_if; + PacketPassFairQueueFlow keepalive_qflow; + int keepalive_sending; + int have_server; + PacketStreamSender send_sender; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + DebugObject d_obj; +} UdpGwClient; + +struct UdpGwClient_conaddr { + BAddr local_addr; + BAddr remote_addr; +}; + +struct UdpGwClient_connection { + UdpGwClient *client; + struct UdpGwClient_conaddr conaddr; + uint8_t first_flags; + const uint8_t *first_data; + int first_data_len; + uint16_t conid; + BPending first_job; + BufferWriter *send_if; + PacketProtoFlow send_ppflow; + PacketPassFairQueueFlow send_qflow; + BAVLNode connections_tree_by_conaddr_node; + BAVLNode connections_tree_by_conid_node; + LinkedList1Node connections_list_node; +}; + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) WARN_UNUSED; +void UdpGwClient_Free (UdpGwClient *o); +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) WARN_UNUSED; +void UdpGwClient_DisconnectServer (UdpGwClient *o); + +#endif diff --git a/app/src/main/jni/libancillary/API b/app/src/main/jni/libancillary/API new file mode 100644 index 0000000..b558995 --- /dev/null +++ b/app/src/main/jni/libancillary/API @@ -0,0 +1,139 @@ + This library provide an easy interface to the black magic that can be done + on Unix domain sockets, like passing file descriptors from one process to + another. + + Programs that uses this library should include the ancillary.h header file. + Nothing else is required. + + All functions of this library require the following header: + + #include + + At this time, the only ancillary data defined by the Single Unix + Specification (v3) is file descriptors. + +Passing file descriptors + + int ancil_send_fd(socket, file_descriptor) + int socket: the Unix socket + int file_descriptor: the file descriptor + Return value: 0 for success, -1 for failure. + + Sends one file descriptor on a socket. + In case of failure, errno is set; the possible values are the ones of the + sendmsg(2) system call. + + + int ancil_recv_fd(socket, file_descriptor) + int socket: the Unix socket + int *file_descriptor: pointer to the returned file descriptor + Return value: 0 for success, -1 for failure + + Receives one file descriptor from a socket. + In case of success, the file descriptor is stored in the integer pointed + to by file_descriptor. + In case of failure, errno is set; the possible values are the ones of the + recvmsg(2) system call. + The behavior is undefined if the recv_fd does not match a send_fd* on the + other side. + + + int ancil_send_fds(socket, file_descriptors, num_file_descriptors) + int socket: the Unix socket + const int *file_descriptors: array of file descriptors + unsigned num_file_descriptors: number of file descriptors + Return value: 0 for success, -1 for failure + + Sends several file descriptors on a socket. + In case of failure, errno is set; the possible values are the ones of the + sendmsg(2) system call. + The maximum number of file descriptors that can be sent using this + function is ANCIL_MAX_N_FDS; the behavior is undefined in case of + overflow, probably a stack corruption. + + + int ancil_recv_fds(socket, file_descriptors, num_file_descriptors) + int socket: the Unix socket + int *file_descriptors: return array of file descriptors + unsigned num_file_descriptors: number of file descriptors + Return value: number of received fd for success, -1 for failure + + Receives several file descriptors from a socket, no more than + num_file_descriptors. + In case of success, the received file descriptors are stored in the array + pointed to by file_descriptors. + In case of failure, errno is set; the possible values are the ones of the + recvmsg(2) system call. + The maximum number of file descriptors that can be received using this + function is ANCIL_MAX_N_FDS; the behavior is undefined in case of + overflow, probably a stack corruption. + The behavior is undefined if the recv_fds does not match a send_fd* on + the other side, or if the number of received file descriptors is more than + num_file_descriptors. + + + int ancil_send_fds_with_buffer(socket, fds, num, buffer) + int socket: the Unix socket + const int *fds: array of file descriptors + unsigned num: number of file descriptors + void *buffer: buffer to hold the system data structures + Return value: 0 for success, -1 for failure + + Sends several file descriptors on a socket. + In case of failure, errno is set; the possible values are the ones of the + sendmsg(2) system call. + The buffer argument must point to a memory area large enough to hold the + system data structures, see ANCIL_FD_BUFFER. + + + int ancil_send_fds_with_buffer(socket, fds, num, buffer) + int socket: the Unix socket + int *fds: return array of file descriptors + unsigned num: number of file descriptors + void *buffer: buffer to hold the system data structures + Return value: number of received fd for success, -1 for failure + + Receives several file descriptors from a socket, no more than + num_file_descriptors. + In case of success, the received file descriptors are stored in the array + pointed to by file_descriptors. + In case of failure, errno is set; the possible values are the ones of the + recvmsg(2) system call. + The behavior is undefined if the recv_fds does not match a send_fd* on + the other side, or if the number of received file descriptors is more than + num_file_descriptors. + The buffer argument must point to a memory area large enough to hold the + system data structures, see ANCIL_FD_BUFFER. + + + ANCIL_MAX_N_FDS + + Maximum number of file descriptors that can be sent with the sent_fds and + recv_fds functions. If you have to send more at once, use the + *_with_buffer versions. The value is enough to send "quite a few" file + descriptors. + + + ANCIL_FD_BUFFER(n) + int n: number of file descriptors + + Expands to a structure data type large enough to hold the system data + structures for n file descriptors. So the address of a variable declared + of type ANCIL_FD_BUFFER(n) is suitable as the buffer argument for + *_with_buffer on n file descriptors. + To use this macro, you need and . Bevare: with + Solaris, the _XPG4_2 macro must be defined before sys/socket is included. + + +Tuning the compilation + + This library is designed to be included in projects, not installed in + /usr/lib. If your project does not use some of the functions, the + TUNE_OPTS variable in the Makefile allows not to build them. It is a list + of proprocessor options: + + -DNDEBUG: turn assertions off (see assert(3)) + -DSPARE_SEND_FDS: do not build ancil_send_fds + -DSPARE_SEND_FD: do not build ancil_send_fd + -DSPARE_RECV_FDS: do not build ancil_recv_fds + -DSPARE_RECV_FD: do not build ancil_recv_fd diff --git a/app/src/main/jni/libancillary/COPYING b/app/src/main/jni/libancillary/COPYING new file mode 100644 index 0000000..5bcd9c2 --- /dev/null +++ b/app/src/main/jni/libancillary/COPYING @@ -0,0 +1,21 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/src/main/jni/libancillary/Makefile b/app/src/main/jni/libancillary/Makefile new file mode 100644 index 0000000..3d32533 --- /dev/null +++ b/app/src/main/jni/libancillary/Makefile @@ -0,0 +1,73 @@ +########################################################################### +# libancillary - black magic on Unix domain sockets +# (C) Nicolas George +# Makefile - guess what +########################################################################### + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +CC=gcc +CFLAGS=-Wall -g -O2 +LDFLAGS= +LIBS= +AR=ar +RANLIB=ranlib +RM=rm +CP=cp +MKDIR=mkdir +TAR=tar +GZIP=gzip -9 + +NAME=libancillary +DISTRIBUTION=API COPYING Makefile ancillary.h fd_send.c fd_recv.c test.c +VERSION=0.9.1 + +OBJECTS=fd_send.o fd_recv.o + +TUNE_OPTS=-DNDEBUG +#TUNE_OPTS=-DNDEBUG \ + -DSPARE_SEND_FDS -DSPARE_SEND_FD -DSPARE_RECV_FDS -DSPARE_RECV_FD + +.c.o: + $(CC) -c $(CFLAGS) $(TUNE_OPTS) $< + +all: libancillary.a + +libancillary.a: $(OBJECTS) + $(AR) cr $@ $(OBJECTS) + $(RANLIB) $@ + +fd_send.o: ancillary.h +fd_recv.o: ancillary.h + +test: test.c libancillary.a + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) -L. test.c -lancillary $(LIBS) + +clean: + -$(RM) -f *.o *.a test + +dist: + $(MKDIR) $(NAME)-$(VERSION) + $(CP) $(DISTRIBUTION) $(NAME)-$(VERSION) + $(TAR) -cf - $(NAME)-$(VERSION) | $(GZIP) > $(NAME)-$(VERSION).tar.gz + $(RM) -rf $(NAME)-$(VERSION) diff --git a/app/src/main/jni/libancillary/ancillary.h b/app/src/main/jni/libancillary/ancillary.h new file mode 100644 index 0000000..636d867 --- /dev/null +++ b/app/src/main/jni/libancillary/ancillary.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * ancillary.c - public header + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANCILLARY_H__ +#define ANCILLARY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************************** + * Start of the readable part. + ***************************************************************************/ + +#define ANCIL_MAX_N_FDS 960 +/* + * Maximum number of fds that can be sent or received using the "esay" + * functions; this is so that all can fit in one page. + */ + +extern int +ancil_send_fds_with_buffer(int, const int *, unsigned, void *); +/* + * ancil_send_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds_with_buffer(int, int *, unsigned, void *); +/* + * ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success + */ + +#define ANCIL_FD_BUFFER(n) \ + struct { \ + struct cmsghdr h; \ + int fd[n]; \ + } +/* ANCIL_FD_BUFFER(n) + * + * A structure type suitable to be used as buffer for n file descriptors. + * Requires . + * Example: + * ANCIL_FD_BUFFER(42) buffer; + * ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer); + */ + +extern int +ancil_send_fds(int, const int *, unsigned); +/* + * ancil_send_fds(sock, n_fds, fds) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds(int, int *, unsigned); +/* + * ancil_recv_fds(sock, n_fds, fds) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * *n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success. + */ + + +extern int +ancil_send_fd(int, int); +/* ancil_recv_fd(sock, fd); + * + * Sends the file descriptor fd on the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fd(int, int *); +/* ancil_send_fd(sock, &fd); + * + * Receives the file descriptor fd from the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ANCILLARY_H__ */ diff --git a/app/src/main/jni/libancillary/fd_recv.c b/app/src/main/jni/libancillary/fd_recv.c new file mode 100644 index 0000000..46c2e69 --- /dev/null +++ b/app/src/main/jni/libancillary/fd_recv.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - receiving file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "ancillary.h" + +int +ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = -1; + + if(recvmsg(sock, &msghdr, 0) < 0) + return(-1); + for(i = 0; i < n_fds; i++) + fds[i] = ((int *)CMSG_DATA(cmsg))[i]; + n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); + return(n_fds); +} + +#ifndef SPARE_RECV_FDS +int +ancil_recv_fds(int sock, int *fd, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer)); +} +#endif /* SPARE_RECV_FDS */ + +#ifndef SPARE_RECV_FD +int +ancil_recv_fd(int sock, int *fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1); +} +#endif /* SPARE_RECV_FD */ diff --git a/app/src/main/jni/libancillary/fd_send.c b/app/src/main/jni/libancillary/fd_send.c new file mode 100644 index 0000000..01de87f --- /dev/null +++ b/app/src/main/jni/libancillary/fd_send.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - sending file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "ancillary.h" + +int +ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing = '!'; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = fds[i]; + return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1); +} + +#ifndef SPARE_SEND_FDS +int +ancil_send_fds(int sock, const int *fds, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer)); +} +#endif /* SPARE_SEND_FDS */ + +#ifndef SPARE_SEND_FD +int +ancil_send_fd(int sock, int fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer)); +} +#endif /* SPARE_SEND_FD */ diff --git a/app/src/main/jni/libancillary/test.c b/app/src/main/jni/libancillary/test.c new file mode 100644 index 0000000..d3c1fda --- /dev/null +++ b/app/src/main/jni/libancillary/test.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * test.c - testing and example program + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ancillary.h" + +void child_process(int sock) +{ + int fd; + int fds[3], nfds; + char b[] = "This is on the received fd!\n"; + + if(ancil_recv_fd(sock, &fd)) { + perror("ancil_recv_fd"); + exit(1); + } else { + printf("Received fd: %d\n", fd); + } + write(fd, b, sizeof(b)); + close(fd); + sleep(2); + + nfds = ancil_recv_fds(sock, fds, 3); + if(nfds < 0) { + perror("ancil_recv_fds"); + exit(1); + } else { + printf("Received %d/3 fds : %d %d %d.\n", nfds, + fds[0], fds[1], fds[2]); + } +} + +void parent_process(int sock) +{ + int fds[2] = { 1, 2 }; + + if(ancil_send_fd(sock, 1)) { + perror("ancil_send_fd"); + exit(1); + } else { + printf("Sent fd.\n"); + } + sleep(1); + + if(ancil_send_fds(sock, fds, 2)) { + perror("ancil_send_fds"); + exit(1); + } else { + printf("Sent two fds.\n"); + } +} + +int main(void) +{ + int sock[2]; + + if(socketpair(PF_UNIX, SOCK_STREAM, 0, sock)) { + perror("socketpair"); + exit(1); + } else { + printf("Established socket pair: (%d, %d)\n", sock[0], sock[1]); + } + + switch(fork()) { + case 0: + close(sock[0]); + child_process(sock[1]); + break; + case -1: + perror("fork"); + exit(1); + default: + close(sock[1]); + parent_process(sock[0]); + wait(NULL); + break; + } + return(0); +} diff --git a/app/src/main/jni/pdnsd/AUTHORS b/app/src/main/jni/pdnsd/AUTHORS new file mode 100644 index 0000000..fa0454e --- /dev/null +++ b/app/src/main/jni/pdnsd/AUTHORS @@ -0,0 +1,58 @@ +Most of pdnsd was written by Thomas Moestl (tmoestl@gmx.net). +In the "par" versions large parts of the code have been revised +and several features have been added by Paul Rombouts. + +Small parts of this program are based on code that was taken from nmap (IP +checksumming), the isdn4k-utils (ippp interface uptest), glibc 2.1.2 (some +definitions for kernel 2.2.x missing in 2.0 glibcs) and FreeBSD +(SIZEOF_ADDR_IFREQ in netdev.c). +nmap was written by Fyodor. The insd4k-utils were written by Fritz Elfert and +others. The GNU C library (glibc) is copyright by the Free Software +Foundation. + +The following people have contributed code: +Andrew M. Bishop contributed support for server labels +Carsten Block contributed 'configure'-able rc scripts +Stephan Boettcher contributed the SCHEME= option. +P.J. Bostley contributed patches to get pdnsd working on + alpha +Frank Elsner contributed rc script fixes +Christian Engstler contributed patches for SuSE compatability +Bjoern Fischer contributed code to make pdnsd leave the case of names + in the cache unchanged +Torben Janssen contributed RedHat rc scripts +Olaf Kirch contributed a security fix for the run_as() + function +Bernd Leibing contributed fixes to the spec file. +Sourav K. Mandal contributed the autoconf/automake code, gdbm + caching facility and many suggestions +Markus Mohr contributed Debian rc scripts +Alexandre Nunes contributed autoconf fixes +Wolfgang Ocker contributed the server_ip option +Soenke J. Peters contributed patches and suggestions for RedHat + compatability +Roman Shterenzon contributed many helpful hints and patches for + FreeBSD compatability. +Andreas Steinmetz contributed the code for the query_port_start and + query_port_end options (which I changed slightly, + so blame any breakage on me ;) +Marko Stolle contributed the contrib/pdnsd_update.pl script that + makes pdnsd usable in a DHCP setup. +Lyonel Vincent extended the serve_aliases option to support an + arbitrary number of aliases +Paul Wagland contributed a patches for bind9-compatability + and for some memory leaks on error paths. +Sverker Wiberg contributed IPv6 build fixes +Michael Wiedmann contributed the pdnsd-ctl.8 man page. +Ron Yorston contributed the dev-uptest for Linux ppp dial- + on-demand devices +Nikita V. Youshchenko contributed extensions to the "if" uptest +Mahesh T. Pai contributed the pdnsd.8 man page. +Nikola Kotur contributed the Slackware start-up script. +Kiyo Kelvin Lee contributed a patch for Cygwin support. +Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support. +Jan-Marek Glogowski contributed a patch implementing the "use_nss" option. + +Please look into the THANKS file for people who helped me in various ways on +this project. +If this list is incomplete, pease drop me a mail! diff --git a/app/src/main/jni/pdnsd/COPYING b/app/src/main/jni/pdnsd/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/app/src/main/jni/pdnsd/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/app/src/main/jni/pdnsd/COPYING.BSD b/app/src/main/jni/pdnsd/COPYING.BSD new file mode 100644 index 0000000..99fe14a --- /dev/null +++ b/app/src/main/jni/pdnsd/COPYING.BSD @@ -0,0 +1,26 @@ +A small part of the pdnsd source is licensed under the following BSD-style +license: + +Copyright (C) 2001 Thomas Moestl + +This file is part of the pdnsd package. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/src/main/jni/pdnsd/ChangeLog b/app/src/main/jni/pdnsd/ChangeLog new file mode 100644 index 0000000..fe77465 --- /dev/null +++ b/app/src/main/jni/pdnsd/ChangeLog @@ -0,0 +1,3304 @@ +2012-04-23 Paul A. Rombouts + + * src/dns_query.c + Refine the return values of p_dns_cached_resolve(), p_dns_resolve() and + p_recursive_query() so that they distinguish between answers found in + the cache and replies obtained by querying other servers. + This, among other things, can be used to prevent data that was recently + obtained from the cache needlessly being added back to the cache. + +2012-04-22 Paul A. Rombouts + + * configure.in + On the Linux platform, check if we can compile and link with the + -pthread flag instead of linking with -lpthread. + +2012-04-21 Paul A. Rombouts + + * src/dns_query.c + When following the delegation chain trying to get an authoritative + answer, pdnsd would answer with SERVFAIL if it failed to get a reply + from the last server in the chain. Instead pdnsd will now use the last + reply in the chain with RCode=0 that raised the AA or RA flag, if there + is one. + +2012-04-19 Paul A. Rombouts + + * src/cache.c + In report_cache_stat(), make copies of volatile data to get a + consistent data set before making calculations with cache size and + entry numbers. + +2012-04-16 Paul A. Rombouts + + * src/netdev.c + If we can't open /proc/net/if_inet6 in is_local_addr() log a warning + message. + +2012-04-15 Paul A. Rombouts + + * src/dns_query.c + The code checking for duplicate IP addresses obtained from NS records + in auth_ok() has been slightly optimized. + +2012-04-12 Paul A. Rombouts + + * src/dns_query.c + When resolving nameservers obtained from NS records, allow pdnsd to use + more than one IP address per nameserver. + In rare cases, using just one IP address for each nameserver will cause + unnecessary resolve failures if the address chosen for each nameserver + happens to be unreachable while the other addresses would lead to + successful resolution, as demonstrated by Yuri Vorobyev. + +2012-03-16 Paul A. Rombouts + + * src/cache.c + When adding RR records one by one to a cache entry using add_cent_rr(), + use the smallest ttl value in case of conflicting ttls. + Code for local/nonlocal conflict resolution has been taken out of + add_cent_rr_int() and put into add_cent_rr() and cr_check_add() + which should be slightly more efficient. + +2012-03-15 Paul A. Rombouts + + * src/dns_query.c + Enforcing strict RFC 2181 compliance by rejecting all the answers + with inconsistent ttl timestamps can cause undesirable resolve failures. + I have tried to implement a more compromising solution, whereby + inconsistent answers that should be normally rejected are still never + cached, but are nevertheless used as intermediary or temporary results + if all else fails. + +2012-03-13 Paul A. Rombouts + + * src/dns_query.c + Fixed a typo in rr_to_cache() that caused pdnsd to fail to compile when + configured with the --enable-strict-rfc2181 option. + Thanks to Gonzalo L. R. for reporting this problem. + Also changed the return value of rr_to_cache() from a simple boolean to + an RC code in order to properly distinguish between memory allocation + errors and time-stamp inconsistencies. + +2012-02-21 Paul A. Rombouts + + * src/dns_query.c + If we have used EDNS in a query and the remote server answered + with rcode "format error", try again with the OPT pseudo-record + removed from the additional section of the query. + + Also fixed a bug in p_exec_query() that caused pdnsd to behave + as if every reply with a non-empty additional section contained + an OPT record. + +2012-02-15 Paul A. Rombouts + + * src/dns_answer.c,src/helpers.c,src/helpers.h,src/icmp.c, + src/ipvers.h,src/main.c,src/netdev.c + Introduced a new macro SEL_IPVER() to reduce some of the clutter in the + code caused by having to support both IPv4 and IPv6. + +2012-01-31 Paul A. Rombouts + + * configure.in + Add AM_PROG_CC_C_O line to configure.in to prevent automake warning. + +2012-01-29 Paul A. Rombouts + + * src/cache.c + In report_cache_stat(), add the average number of bytes used per cache + entry when reporting the cache status, as suggested by M. Galabant. + +2012-01-28 Paul A. Rombouts + + * src/dns_answer.c,src/dns_query.c + Cleaned up the code a bit to avoid warning messages when + compiling with '-Wall -Winline' flags. + +2012-01-18 Paul A. Rombouts + + * src/conff.c + Set the default of the edns_query option to false. + +2011-07-31 Paul Rombouts + + * src/cache.c + Use a slightly more sophisticated merge-sort algorithm in sort_rrl(). + +2011-05-09 Paul Rombouts + + * src/dns_answer.c + In compose_answer(), also add an OPT pseudo-RR to the additional section + of a NXDOMAIN reply when appropriate. + +2011-05-08 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_query.c,src/status.c + Make the dns_cent_t struct more compact by putting the fields that are + only used for either non-existent or existent domains, but not both, + into a union so that these fields can share memory. + When saving the cache to file, only write the TTL and time-stamp for + a whole domain when it is negatively cached. + +2011-05-06 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_query.c + At the request of Andrei Caraman, the TTL of a negatively cached domain + is now adjusted in accordance with the min_ttl and max_ttl options, just + as it is done for (negatively) cached records. + Additional change to the TTL policy is that for negative records (and + negative domains) the neg_ttl setting overrides min_ttl if + neg_ttl < min_ttl. + +2011-04-26 Paul Rombouts + + * src/conf-parser.c + Fixed memory leak that can occur when the configuration file is reloaded + and an error is encountered while parsing the definition of a TXT + record. + +2011-03-21 Paul Rombouts + + * src/make_rr_types_h.pl,src/cache.h,src/cache.c,src/dns_answer.c + Introduced arrays rrmuiterlist and rrcachiterlist to make iterating + over all possible RR types in a cache entry in strict ascending order + a little more efficient. + +2011-03-09 Paul Rombouts + + * src/dns_query.c,src/conf-parser.c,src/conf-keywords.h + Implemented a new config option "outgoing_ip", which + makes it possible to bind outgoing connections to + a specific interface. + +2011-02-21 Paul Rombouts + + * src/netdev.c + Fixed UDP socket descriptors leak in the implementation of + is_local_addr() for the FreeBSD platform. Thanks to Ashish Shukla for + reporting this bug. + +2011-02-14 Paul Rombouts + + * src/cache.c + In purge_all_rrsets(), also free the rrext array if it has become empty after + purging all the RR sets. + +2011-02-04 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/conf-keywords.h, + src/dns_query.c,src/dns_query.h,src/servers.c + Changed "edns_query" from a "global" option to a "server" + configuration option. + +2011-02-04 Paul Rombouts + + * src/conff.c,src/conff.h,src/dns_query.c,src/dns_query.h,src/servers.c, + src/conf-parser.c + The query uptest sometimes fails because some DNS servers are configured + to ignore empty queries. The new config option "query_test_name" makes + it possible to query for a specific name instead. + +2011-02-01 Paul Rombouts + + * src/dns_query.c + When processing a reply from a remote name server which seems to delegate + to other name servers, check if the names for which NS records have + been supplied have locally defined NS records. If so, the local + records will now override those supplied by the remote server. + +2011-01-31 Paul Rombouts + + * src/conf-parser.c + Added support for defining TXT records in the configuration file. + +2011-01-30 Paul Rombouts + + * src/dns_query.c + Do not cache additional records from a response that is rejected because + it contains IP addresses in the reject list, even when the reply + is processed as a NXDOMAIN reply. + +2011-01-25 Paul Rombouts + + * src/conf-parser.c + Modified the function scan_string() to allow back-slashed escape + sequences in strings. + +2011-01-21 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/conff.h,src/conff.c, + src/conf-parser.c + Added support for EDNS (Extension mechanisms for DNS). + Currently this is only useful for allowing UDP message sizes + to be larger than 512 bytes. + +2011-01-20 Paul Rombouts + + * src/dns_answer.c + To avoid frequent reallocs when composing a DNS reply message, + grow the message buffer in multiples of a certain minimum chunk size. + +2011-01-19 Paul Rombouts + + * src/dns.c,src/dns.h,src/dns_answer.c + Extended debugging info with DNS-message lengths and flags of incoming + messages. + +2011-01-17 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/dns_answer.c + Made "ignore_cd" option obsolete. It is now effectively always on. + +2010-12-27 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_answer.c,src/dns_query.c, + src/make_rr_types.pl,src/rr_types.in,src/rr_types.c + The array of pointers to rr_set_t structs in the dns_cent_t struct + contains mostly null pointers in practice, so is somewhat + inefficient in storage usage. This problem is exacerbated if we add + support for caching more RR-types. To ameliorate to the problem + I have decided to split the array in two, with one part fixed in the + dns_cent_t struct as before, and an extension part that will be + separately allocated, if necessary. If the extension part is used only + for very rarely cached types, in most cases the extension array will not + need to be allocated thus hopefully saving memory overall. + The lookup tables which are necessary to support the new cache entry + structure are cumbersome to write by hand, so I have written a perl + script to do this automatically. As an additional benefit, which RR + types are cache-able is now configurable for each type separately via + rr_types.in. + +2010-03-14 Paul Rombouts + + * src/dns_query.c + Using randomized source ports for outgoing queries in IPv6 mode failed + with the warning "Out of ports in the range 1024-65535, dropping query!", + because the pdnsd tried to bind to the fixed port for incoming queries, + instead of the dynamically chosen port. This is a very old bug, but it + has only become apparent since source port randomization has become the + default. + Thanks to Philip-André Fillion, Phil Sutter, Radoslaw Szkodzinski and + others for reporting this bug and sending patches. + +2009-12-25 Paul Rombouts + + * src/status.c,src/status.h,src/pdnsd-ctl/pdnsd-ctl.c + Add a magic number to pdnsd-ctl command codes to guard against + possible incompatibility between the pdnsd-ctl utility and the + pdnsd server. + +2009-10-18 Paul Rombouts + + * src/dns_query.c + Make root-server discovery a little more fault tolerant, i.e. if some + of the root-server names don't resolve don't necessarily reject the + whole result. + +2009-10-17 Paul Rombouts + + * src/servers.c,src/dns_query.c,src/dns_query.h + Implemented automatic root-server discovery, which can now be configured + by setting "root_server=discover". + +2009-06-14 Paul Rombouts + + * src/dns_query.c,src/consts.c,src/consts.h,src/conf-parser.c + Changed the default behaviour of the "neg_rrs_pol" option. The default + used to be to only cache records negatively in case the AA (authoritive + answer) bit in the reply was set. The new default is to also allow + negative caching in case the reply has the RA (recursion available) bit + set and the query had the RD (recursion desired) bit set. + This gives the behaviour that is usually wanted in case "proxy_only=on" + is set without having to set "neg_rrs_pol=on", which can be more + problematic. The new default can be explicitly set using + "neg_rrs_pol=default". The values "on","off" and "auth" are also + still available. + +2009-06-13 Paul Rombouts + + * src/conff.c,src/conff.h,src/dns_answer.c,src/conf-parser.c,src/conf-keywords.h + Included a patch contributed by Andreas Steinmetz that implements a new + global configuration option "ignore_cd". pdnsd used to check that the CD + bit in the DNS header of queries is zero and return the error code + "format error" if it is not. However, considering the meaning of this + bit today it appears to be harmless to ignore it, so the new "ignore_cd" + is on by default. Setting "ignore_cd=off" gives the earlier strict + behavior. + Also renamed the the Z1, AU, Z2 bits to correspond with their modern names + CD, AD, Z. + +2008-12-19 Paul Rombouts + + * pdnsd-1.2.7/src/dns_query.c + If pdnsd receives a SERVFAIL response with a non-empty answer section, + use the information tentatively if no better response is available. + The previous behaviour was to discard the reply completely, which could + cause failure to resolve some names. + Thanks to Rafal Wijata for providing an example involving PowerDNS servers + replying with CNAME records. + +2008-09-01 Paul Rombouts + + * src/dns_query.c + In p_dns_resolve(), try to reduce the burden on root servers further for + names ending in "arpa". + +2008-08-31 Paul Rombouts + + * src/dns_query.c + In p_exec_query(), if the reply from a remote name server is negative + (either because the rcode is NXDOMAIN or because the answer section + contains no records for the queried name), ignore the remaining records + in the answer section (in particular do not add them to the cache). + +2008-07-29 Paul Rombouts + + * src/conff.c,src/dns_query.c + Made the default of the configuration option query_port_start equal to + 1024. Also improved the algorithm used by pdnsd to select random source + ports to ensure that each (free) port gets an equal chance of being + selected. This should guarantee random source ports in the range + 1024-65535, making pdnsd less vulnerable to some of the issues described + in CERT VU#800113. + The old situation, where pdnsd lets the kernel select the source ports, + is still available by specifying query_port_start=none. + +2008-07-25 Paul Rombouts + + * src/dns_query.c + Fixed a dangling pointer bug in p_exec_query(), which could cause pdnsd + to crash when processing a long reply with many entries in the answer + section. + +2008-05-12 Paul Rombouts + + * src/conf-parser.c,src/conff.c + Added a recursive-depth counter to the read_config_file() and + confparse() functions to prevent the possibility of infinite + recursion when processing include files. + In confparse(), warn when in a server section the root_server option is + set in combination with policy=simple_only or policy=fqdn_only. + +2008-05-10 Paul Rombouts + + * src/ipvers.h + Included a patch contributed by Georg Schwarz which selectively undoes + a Debian patch contributed by Juliusz Chroboczek on platforms for which + the IPV6_RECVPKTINFO macro is not defined (e.g. MacOS X). + +2008-05-08 Paul Rombouts + + * src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + The pdnsd-ctl add command can now also be used to define NS records. + A wildcard record defined with this command now behaves the same way as + one defined in the config file. + +2008-05-07 Paul Rombouts + + * src/conf-parser.c,src/conf-keywords.h,src/conff.c + Added the ability to process "include" sections in the configuration + file. This makes it possible to place local definitions in separate + files and include them from the main configuration file. + +2008-05-05 Paul Rombouts + + * src/conff.c,src/conf-parser.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + Implemented two new pdnsd-ctl commands, which make it easier to add + definitions to the pdnsd cache at run time. "pdnsd-ctl include" is + similar to "pdnsd-ctl config" but only processes configuration sections + that effect the cache and disallows global and server sections. + "pdnsd-ctl eval" directly parses its string arguments as if they were + part of a configuration (include) file. + +2007-09-15 Paul Rombouts + + * src/dns.h,src/dns_answer.c,src/dns_query.c + Changed the declarations of various packed structs, by moving the + __attribute__((packed)) specifiers from the field level to the struct level. + This was necessary to get the correct value for sizeof(rr_hdr_t) when + compiling with gcc for the ARM architecture. + Thanks to Dirk Armbrust for reporting the problem and supplying the solution. + +2007-08-10 Paul Rombouts + + * src/dns_answer.c + Applied a Debian patch contributed by Juliusz Chroboczek which + reportedly fixes a problem with pdnsd running in IPv6 mode + (IPV6_RECVPKTINFO instead of IPV6_PKTINFO). + +2007-08-04 Paul Rombouts + + * src/dns_query.c + When resolving a name recursively, pdnsd would stop querying further + name servers as soon as it received a reply with the authority (aa) flag + set. Unfortunately, it appears this flag is sometimes raised erroneously + in replies. I have implemented a work-around that ignores the aa flag + when there appears to be a clear delegation to a sub-domain. + Thanks to Nico Erfurth for reporting this problem. + + It appears that pdnsd would also fail to consult servers in the authority + section when configured with neg_rrs_pol=on. This has been fixed. + +2007-08-01 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Made the matching of pdnsd-ctl command names and most of the arguments + case-insensitive. + +2007-07-22 Paul Rombouts + + * src/dns_answer.c + Instead of sharing the responsibility for freeing the answer buffer in + case of an error amongst different functions, only free it in + compose_answer(). + + * configure.in, src/Makefile.am, src/test/Makefile.am + Merged patch contributed by Pierre Habouzit to deal with CFLAGS the + automake way (allowing packagers to override CFLAGS properly). + +2007-07-21 Paul Rombouts + + * src/dns_answer.c + For each target name in a SRV record in the answer section, add + addresses to the additional section of the response, as is recommended + by the RFCs. + +2007-07-14 Paul Rombouts + + * src/list.c,src/list.h + Made modifications to the implementation of dynamic arrays, which + should ensure proper alignment on all supported architectures. + +2007-07-10 Paul Rombouts + + * Upgraded pdnsd's license to GPL version 3. + +2007-07-08 Paul Rombouts + + * src/cache.h,src/dns_query.c + The data field of the rr_bucket_t struct is now aligned such that + it possible to use straightforward assignment to copy IP addresses, + making memcpy unnecessary for this purpose. + +2007-07-07 Paul Rombouts + + * src/dns_query.c + If pdnsd fails to connect to a name server using a IPv6 address, it will + now retry the connection using a IPv4 address, if available. This allows + pdnsd to recover from situations where IPv6 connectivity is temporarily + unavailable, but IPv4 connectivity still functions. + Thanks to Andreas Ferber for reporting this problem. + +2007-07-04 Paul Rombouts + + * src/dns_answer.c + I have reordered the arguments of the add_rr() and related + functions to make them more consistent with each other. + +2007-07-03 Paul Rombouts + + * src/cache.c,src/hash.c + pdnsd will no longer immediately abort in add_dns_hash() if it fails + to allocate memory for a new hash entry. + +2007-07-01 Paul Rombouts + + * src/conff.c,src/conff.h,src/consts.c,src/consts.h, + src/conf-parser.c,src/conf-keywords.h,src/dns_query.c + Implemented the new "reject", "reject_policy" and "reject_recursively" + options for the server section of the configuration file. + + * src/ipvers.h,src/conf-parser.c,src/dns.c,src/status.c, + src/pdnsd-ctl/pdnsd-ctl.c + Allow local AAAA records to be defined even if pdnsd is compiled + without --enable-ipv6, provided there is sufficient support in the + C libraries and --disable-new-rrs was not used. + +2007-06-30 Paul Rombouts + + * src/dns_answer.c + Previously, when the answer buffer was realloced in add_rr(), an + extra 2 bytes used to be reserved, which are unnecessary, as far + as I can tell. I have decided to do without these extra 2 bytes, + which originate from Thomas Moestl's code. As compensation, I have + added extra PDNSD_ASSERT() statements to check that the answer + buffer does not overflow. + +2007-06-27 Paul Rombouts + + * src/status.c, src/pdnsd-ctl/pdnsd-ctl.c + Extended the pdnsd-ctl 'add a' and 'add aaaa' commands to allow + multiple IP addresses to be specified. + +2007-06-25 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/conf-keywords.h, + src/dns_query.c + Implemented a new option for the server section of the configuration + file: randomize_servers. + + * src/servers.c + Improved the debug messages in uptest(). + +2007-01-30 Paul Rombouts + + * src/icmp.c + Fixed up the code implementing the ping test in icmp.c, + which was broken for 64-bit systems. + Thanks to Michael Uleysky for reporting this bug. + +2007-01-09 Paul Rombouts + + * src/dns_query.c + auth_ok() now returns 1 if the cache entry has the DF_NEGATIVE flag set, + without providing a list of authoritative servers to continue querying. + Otherwise if we receive a non-authoritative NXDOMAIN reply and pdnsd + is configured with neg_domain_pol=on, pdnsd will continue to try to + get an authoritative answer. The intention is that pdnsd + stops querying as soon as it gets an "unknown domain" answer. + +2006-04-29 Paul Rombouts + + * src/main.c + pdnsd would segfault if it tried to call log_message() (via the + log_warn() and log_error() macros) before the FILE pointer to the debug + output stream was properly initialized. + Thanks to Thomas Cort for discovering this problem and suggesting a fix. + +2006-04-09 Paul Rombouts + + * src/conf-parser.c,src/helpers.c,src/conff.h,src/conff.c + I have included a patch contributed by Jan-Marek Glogowski, that + implements the configuration option "use_nss". With use_nss=off pdnsd + will avoid system functions that may use NSS (i.e. initgroups()), which + may need DNS for LDAP lookups, which can lead to long timeouts and + stalls if pdnsd itself is used for the DNS lookup. + +2006-03-26 Paul Rombouts + + * src/dns_query.c + Negative caching of RR sets is now also supported with lean_query=off. + +2006-03-25 Paul Rombouts + + * src/dns_query.c,src/conf-parser.c,src/main.c + I have implemented a new query method: udp_tcp. With this method a UDP + query is tried first and, if the UDP answer is truncated, the query is + repeated using TCP. This is the behaviour that seems to be recommended + by the DNS standards. However, pdnsd wil not discard the truncated + answer if the TCP requery fails. + +2006-03-24 Paul Rombouts + + * src/dns_answer.c + Previously, pdnsd would add at most one additional A (and AAA) record + for each record in the answer and authority sections. At the request of + Angel Marin, pdnsd will now add all A and AAA records it can find in the + cache for each name that produces additional records. + +2006-01-02 Paul Rombouts + + * src/dns_answer.c + compose_answer() would leak memory if the query contained + an unsupported QTYPE or QCLASS. This has now been fixed. + +2005-12-27 Paul Rombouts + + * configure.in + TCP-query support is now compiled in by default. + It can still be disabled using the configure option + --disable-tcp-queries. + +2005-12-23 Paul Rombouts + + * src/dns_answer.c + Queries received from clients with non-empty answer, authority or + additional sections are now treated as malformed and rejected with + rcode 1 (format error). + +2005-11-06 Paul Rombouts + + * src/conf-parser.c + Time intervals in the configuration files can now be expressed in + seconds, minutes, hours, days and weeks, using the suffixes + s,m,h,d,and w. + +2005-10-14 Paul Rombouts + + * src/consts.c + In the pdnsd configuration file, true/false and yes/no are now accepted + as synonyms for the constants on/off. + +2005-08-24 Paul Rombouts + + * src/helpers.c + I have fixed a potential buffer overflow problem that could occur with + the 'pdnsd-ctl dump' command. + In case of the root domain, the function rhn2str() would write 2 bytes + to the output buffer even if size==1. Theoretically (under pathological + circumstances) this could have allowed the dbuf buffer in the function + dump_cent() to overflow by one byte. + +2005-08-21 Paul Rombouts + + * acconfig.h,src/cache.c,src/conff.c,src/conf-parser.c,src/dns.c, + src/dns_answer.c,src/dns_query.c,src/error.h,src/helpers.c,src/main.c, + status.c + + It appears the newer versions of gcc won't convert a pointer to char + into a pointer to unsigned char and vice versa without complaining. + The changes I have made should get rid of these distracting warning + messages. Unfortunately I had to introduce casts in some cases, + which reduces type safety :-(. + +2005-08-16 Paul Rombouts + + * src/dns.h + Some changes were made to the endianess detection code to + address problems on Mac OS X v10.4 Tiger. + +2005-08-15 Paul Rombouts + + * configure.in + Some changes where made to address the reported problems with the + configure script on Mac OS X v10.4 Tiger. + +2005-08-05 Paul Rombouts + + * src/status.c,src/dns_answer.c + The output of the 'pdnsd-ctl status' command now includes some + statistics on the number of query threads. + +2005-07-29 Paul Rombouts + + * src/main.c + It appears that sigwait() can return EINTR under certain conditions. + This explains the problems reported by Sanjoy Mahajan with strace + and ACPI S3 sleep, which both caused pdnsd to exit prematurely. + The return value of sigwait() is now checked and sigwait() is retried + if the return value is EINTR. + +2005-07-04 Paul Rombouts + + * src/dns_query.c + It appears that some servers that do not support recursive queries + answer with "query refused" instead of "not supported". The + p_exec_query() function now takes that possibility into account. + +2005-07-01 Paul Rombouts + + * src/dns_query.c + In the processing of queries, I will make a distinction between + recoverable errors and non-recoverable ones (typically caused by out of + memory conditions). In the case of non-recoverable errors, no attempt to + query alternative name servers is made. + +2005-06-26 Paul Rombouts + + * src/dns_query.c + In p_recursive_query(), as soon as one of the servers in the q list + replied "no error" or "name error", only this reply was examined and + the other servers in the q list were ignored. Joshua Coombs has brought + to my attention that this strategy sometimes fails when this reply is not + authoritative and doesn't contain any usable references to name servers + in the authority section. + I have modified p_recursive_query() to allow pdnsd to continue querying + the remaining servers in the q list as long as we haven't received an + authoritative answer or usable authority information. This will allow + pdnsd to arrive at the correct answer in some cases where it would + formerly fail. + +2005-06-25 Paul Rombouts + + * src/status.c + The "pdnsd dump" command may now also be given an argument + consisting of a name beginning with a dot. This will dump information + about all names in the cache ending in the given name. An argument + consisting of a name without a leading dot will only give information + about the exact name, as it did before. + +2005-06-24 Paul Rombouts + + * src/servers.c,src/status.c + All uptests are now conducted by the server status thread. If a retest + is requested via a "pdnsd-ctl server", an existing server status thread + is signaled or a new server status thread is spawned if the old one has + exited. This has the effect that a "pdnsd-ctl server label retest" + command will now return immediately without waiting for the tests to + finish. + +2005-06-20 Paul Rombouts + + * src/conf-parser.c,src/servers.c,src/servers.h + At the request of Al-Junaid Walker I have added a new configuration + option for the uptest interval. With "interval=ontimeout" the server is + not tested at startup/reconfig, or at regular intervals, but only after + a DNS query to a server times out. However, once a server is declared + dead it is never considered again unless it is revived using a + "pdnsd-ctl config" or "pdnsd-ctl server" command. + +2005-06-19 Paul Rombouts + + * src/servers.c,src/dns_query.c,src/icmp.c + During an uptest the server configuration data is locked. Especially + with ping or query uptests of unresponsive servers this means that the + execution of "pdnsd-ctl config" or "pdnsd-ctl server" commands can be + delayed for a long time (or even time out). I have made modifications + that allow a "pdnsd-ctl config" or "pdnsd-ctl server" commands to + interrupt pending uptests to allow these commands to proceed without + delay in most cases. + + * src/thread.h + Use the POSIX sigaction() instead of signal() to install signal handlers. + +2005-06-08 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c + I have defined a struct dns_msg_t that includes a message length field. + In the case of sending a DNS message over TCP, we no longer need a + separate write() call to send the message length. This prevents possible + packet fragmentation. + +2005-06-07 Paul Rombouts + + * src/dns_query.c + The query_method=tcp_udp option only used to work with cooperative name + servers, i.e. servers that either send back a TCP reply or explicitly + refuse the TCP connection request. This wasn't sufficiently satisfactory + in practice, because some name servers are completely unresponsive to TCP + connection requests. I have made modifications to allow pdnsd to try UDP + queries in case TCP connections time out. When a short server timeout is + combined with a global timeout that is at least twice as long, this may + allow a query to a name server that only responds to UDP queries to + succeed with query_method=tcp_udp. + +2005-04-20 Paul Rombouts + + * src/cache.c,src/hash.c,src/conff.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + The "pdnsd-ctl empty-cache" command now accepts additional arguments; + these are interpreted as include/exclude names. During execution of the + command the name of each cache entry is matched against the names in the + include/exclude list. If the name ends in a name to be included, the + cache entry is deleted, otherwise not. + This feature was added at the request of Joshua Coombs. + +2005-04-19 Paul Rombouts + + * src/cache.c, src/hash.c + pdnsd will now (temporarily) unlock the cache between emptying hash + buckets, this should allow pdnsd to remain responsive while executing + the "pdnsd-ctl empty-cache" command. However, this only applies to DNS + queries; pdnsd will not accept any new pdnsd-ctl commands while a + pdnsd-ctl command is still running. + +2005-03-29 Paul Rombouts + + * configure.in, src/hash.h + I have added a new configure option --with-hash-buckets=... + This makes it possible to specify a different number of + hash buckets without editing the source files. + +2005-03-17 Paul Rombouts + + * src/error.c + When running in both daemon and debug mode, print warning and + error messages to debug file as well as the syslog. + +2005-03-15 Paul Rombouts + + * src/dns_answer.c + Only call pthread_setspecific() in debug mode, because + pthread_getspecific() is also only used in debug mode. + If pthread_setspecific() fails, treat this as a non-fatal error. + +2005-03-10 Paul Rombouts + + * configure.in + On Linux systems the configure script will now try to detect automatically + whether the system implements the Native POSIX Thread Library, but + the method is not necessarily foolproof. + + * src/dns.c + Local PTR records generated for resolving numeric IPv6 addresses back into + names, are now based on ip6.arpa instead of ip6.int, because the latter domain + will be phased out eventually. + +2005-03-06 Paul Rombouts + + * Makefile.am,src/cache.c + Create an empty cache-file at install time and don't complain about empty + cache files at start up. + +2005-02-20 Paul Rombouts + + * acconfig.h,configure.in,src/conf-parser.c,src/conff.h,src/dns.h, + src/dns_answer.c,src/dns_query.c,src/error.h,src/helpers.h,src/icmp.c, + src/ipvers.h + + I have applied some changes to the code proposed by Rodney Brown to improve + portability. In particular, pdnsd should now compile on the Darwin platform + (Apple Mac OS X). + To support some of these changes, the source package is now built with a + slightly more modern version of autoconf (2.57) and automake (1.6.3). + +2005-01-29 Paul Rombouts + + * src/dns.c,src/dns_answer.c,src/dns_query.c + + I have added some extra debug code to make it easier to discover the + reason that pdnsd considers a query or reply malformed (format error). + +2005-01-12 Paul Rombouts + + * src/dns.c,src/dns_answer.c,src/dns_query.c + + I have extended some debug code contributed by Kiyo Kelvin Lee to dump + the data received by pdnsd in debug mode (queries from clients, replies + from name servers). Because this will give very verbose debug output, + I've arranged it so that this data dump only occurs if pdnsd has been + configured and compiled with --with-debug=9 and pdnsd has been called + with -v9. + + Additionally, in the case that pdnsd rejects a reply from a name server + because it is not well formed, I have refined the debug messages to + distinguish between format errors due to unexpected truncation and + others kinds of format errors. + +2004-10-30 Paul Rombouts + + * src/rr_types.c + I have included some changes proposed by Joseph Pecquet to address + the compilation problems reported by FreeBSD users. + +2004-10-18 Paul Rombouts + + * acconfig.h,configure.in,src/helpers.c,src/helpers.h,src/dns.h + I have merged a patch for CYGWIN support by Kiyo Kelvin Lee into + my version of the code. + +2004-10-15 Paul Rombouts + + * src/cache.c + Invalidating local records with the pdnsd-ctl did not work the way the + documentation described. An invalidated local record would be always be + purged at the next lookup, thus invalidation would practically have the + same effect as deletion. An invalidated local record is of no use at all and + would occupy space until it is purged during a lookup (but not by purge_cache). + The function invalidate_record() now behaves as the documentation describes, i.e. + invalidation of local records has no effect. + +2004-09-27 Paul Rombouts + + * doc/pdnsd.conf.5.in + A new man page describing the format of the pdnsd config file has been + added to the pdnsd package. I've used a customized Perl script to generate + one automatically from the html documentation. + +2004-09-14 Paul Rombouts + + * src/hash.c + The cache entries in a hash chain are now stored in order of increasing long hash + value. The advantage is that if an name is looked up that is not present in the + cache, this can be done by comparing with only half (on average) of the number + of entries in the hash chain. Not a huge speed up, but still worth while, I think. + Additionally, the number of hash computations for each add_cache() call has + been halved. + +2004-09-11 Paul Rombouts + + * src/cache.c + insert_rrl() will no longer add local records to the rr_l list, because + purge_cache() ignores them anyway. + +2004-09-08 Paul Rombouts + + * src/dns.h,src/cache.c,src/dns_query.c,src/dns_answer.c,src/conf-parser.c + I've started using GETINT16,GETINT32,PUTINT16,PUTINT32 macros, which are based + on the NS_GET/NS_PUT macros that can be found in the BIND source, instead of memcpy + for fetching and storing non-aligned integer data. + +2004-09-08 Paul Rombouts + + * src/cache.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl dump" will print information about all the + entries contained in the cache. + "pdnsd-ctl dump " will only print entries belonging to . + The data fields of the more common rr-types will be printed in human readable + form, the remaining ones in a hexadecimal representation. + With thanks to Dan Jacobson for suggesting this feature. + +2004-08-31 Paul Rombouts + + * src/conf-parser.c + At the suggestion of Dan Tihelka, I have expanded to the server_ip= option + to allow the name of an interface to be specified instead of an IP address. + pdnsd will not bind to the interface name, but will lookup the address the + interface has at start up, and listen on that address. If the address + of the interface changes while pdnsd is running, pdnsd will not notice that. + +2004-08-30 Paul Rombouts + + * src/cache.h,src/cache.c + I've reversed the meaning of the CF_NOAUTH and renamed it CF_AUTH. + I've also added a domain level flag DF_AUTH, which is used to + mark cache entries obtained from authoritave replies in response to + a query of type * (all).. + +2004-08-30 Paul Rombouts + + * src/cache.c + I've changed the format of the cache file. A typical cache entry has empty + sets for most RR types (even more if DNS_NEW_RRS is defined). In the old + format, each empty RR set was represented by a zero byte. + In the new format only non-empty sets are respresented, leading + to a (modest) reduction is size. + +2004-08-28 Paul Rombouts + + * src/conf-parser.c + New option for "rr" sections in the config file: reverse=on/off. + If you want a locally defined name to resolve to a numeric address and vice + versa, you can now achieve this by setting reverse=on before defining the + A record, making it unnecessary to define a seperate PTR record for the reverse + resolving. + +2004-08-20 Paul Rombouts + + * src/cache.h,src/cache.c,src/conf-parser.c,src/dns_query.c + At the request of Daniel Black, I have added support for defining local wildcard records + in pdnsd. The only type supported presently is records beginning with '*.'. + +2004-08-10 Paul Rombouts + + * src/hash.c,src/cache.c,src/dns_query.c,src/dns_answer.c + Sampo Lehtinen has remarked that pdnsd sometimes failed to resolve classless + reversed-delegated IP addresses, and that this has something to do with the fact + that pdnsd did not accept '/' characters in domain names. After reading Sampo's + and Thomas' remarks, and also rfc2317 and some of the rfc's referenced in rfc2317, + I decided pdnsd should place no restrictions at all on the types of characters it + allows in domain names, only on the lengths of the byte sequences. + This led me to make some quite extensive internal changes to pdnsd. Among other + things domain names are now stored in transport format (sequences of bytes preceded + by length bytes) instead of C strings. This is also more efficient because there + is no need any more to convert from one representation to the other, except when + reading the config file, interacting with pdnsd-ctl or running in debug mode. + Conversion between the two representations isn't always possible, though. + For example, domain names in transport format might contain non-printable characters. + These are now printed as escape sequences (three octal digits preceded by a back slash). + Presently there are still restrictions on the characters in the domain names that can + be defined in local records. I doubt this will ever be considered a problem. + +2004-08-02 Paul Rombouts + + * src/dns_query.c + The code for handling NXT records was flawed. A response from a remote server + containing NXT records (even well-formed ones) could cause pdnsd to crash. + The code for handling NAPTR records contained incorrect PDNSD_ASSERT statements, + which could cause pdnsd to abort unnecessarily. + +2004-07-25 Paul A. Rombouts + + * src/list.h,src/list.c,src/dns.c,src/dns_query,src/dns_answer.c + I've noticed that some of the (dynamic) arrays that pdnsd uses are quite sparse. + Instead of using an array structure with elements that are large enough to contain + the largest possible domain name, I've implemented a "list" data structure that + is more compact. The elements of a list can only be accessed sequentially from + beginning to end, but it allows more efficient memory use in case the names are + significantly shorter that the maximum. + +2004-07-22 Paul Rombouts + + * src/conf-parser.c + I've expanded pdnsd's configuration options by adding support in pdnsd for reading + /etc/resolv.conf style files. Instead of specifying IP addresses in a server section, + the option "file=" can be used. + The IP addresses in the lines beginning with "nameserver" will be added to + the list of address for that section, the remaining lines will be ignored. + To avoid the possibility that pdnsd will query itself, local addresses are skipped + (unless pdnsd is configured to listen on a different port number). + +2004-07-21 Paul Rombouts + + * src/cache.h,src/cache.c,src/dns_query.c,src/conf-parser.c + New option for "server" sections in the config file: root_server=on/off. + In case a server section contains only addresses of root servers, which + usually only give the nameservers of top level domains in their reply, + setting root_server=on will enable certain optimizations. This involves using + cached information to reduce queries to the root servers, thus speeding up + the resolving of new names. This option is also necessary to make the + delegation_only option work in combination with root servers. + +2004-07-16 Paul Rombouts + + * src/cache.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl empty-cache" will make pdnsd delete its entire + cache, freeing all entries. This is useful for debugging purposes, or in situations + where you suspect that stale cache entries are causing you problems, but you are not + sure which ones. + +2004-07-11 Paul Rombouts + + * src/cache.c,src/dns_query.c + I've removed the use of the function add_cache_rr_add(), which was used to + add additional RR records to the cache one at a time. I've changed the code + in dns_query.c such that additional (or off-topic) records are first collected + in arrays of dns_cent_t structures, and then added to the cache using add_cache(). + With this approach only one function, viz. add_cache(), is used for adding + new entries to the cache, which I believe leads to a cleaner programming + interface. Added benefit is that query serial numbers are no longer + necessary. + +2004-07-10 Paul Rombouts + + * src/cache.h,src/cache.c,src/dns_query.c,src/dns_answer.c + I've added two new field to the dns_cent_t struct, namely c_ns and c_soa. + These will be used to remember references to NS and SOA records in the authority + sections of replies from remote name servers. + This information can be used by pdnsd to fill in the authority section of its + own reply. + +2004-06-25 Paul Rombouts + + * src/dns_query.c,src/servers.c,src/consts.c + I've added an new server availability test which can be selected with "uptest=query". + This can be useful as an alternative to "uptest=ping" in case the remote server does not + respond to ICMP_ECHO requests at all, which unfortunately is quite common these days. + "uptest=query" causes pdnsd to send an empty query to remote nameservers. Any well-formed + response (apart from SERVFAIL) within the timeout period will be interpreted as a sign that the + server is "up". + In a sense this new availability test can actually be considered more reliable than the + other ones that pdnsd supports. + With thanks to Juliusz Chroboczek for suggesting this feature. + +2004-06-24 Paul Rombouts + + * src/helpers.c + Don't use getpwnam() while we are multi-threaded, because it returns a pointer to + a statically allocated structure. I will use getpwnam_r() instead, which is thread + safe. Unfortunately there seem to be some portability problems with getpwnam_r(). + For those platforms that lack getpwnam_r(), I will keep the old code with getpwnam() + as an alternative. + +2004-06-23 Paul Rombouts + + * src/servers.c + Check that the number of IP addresses in a server section is nonzero before + testing servers for availability. Otherwise pdnsd could crash in debug mode. + +2004-06-21 Paul Rombouts + + * src/conff.c,src/conf-parser.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl config" will make pdnsd re-load its configuration file. + In most cases (but there are still some exceptions) this is preferable + to restarting pdnsd after making changes to the configuration file. + An important advantage is that there should be no perceptible interruption in the dns service + when using the reload command. + An alternative config file can be specified with "pdnsd-ctl config ". + +2004-05-31 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/dns_query.h + I've made an adjustment to p_recursive_query() and related functions, so that + when pdnsd chases name servers in pursuit of authoritative records, it avoids + all the name servers already queried for the same name in the recursive calling + chain, not just the servers most recently used. + Although the hops counter will already break any possible cycles, this will + allow pdnsd to detect pathological cycles earlier and waste less resources. + + * src/cache.c + In add_cache(), don't add empty entries to the cache. Empty cache entries + waste memory and are more persistent than non-empty ones, because purge_cache() + cannot get rid of them. + +2004-05-30 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/icmp.c,src/netdev.c + I've removed the calls to getprotobyname() and used the constants IPPROTO_TCP + and IPPROTO_UDP instead. First of all, it doesn't seem very efficient to call + a function repeatedly to look up the same well-known protocol numbers. + More importantly, getprotobyname() stores its results in a statically-allocated + structure and thus cannot be considered thread safe. (getprotobyname_r() + is thread safe, but is not portable.) + +2004-05-27 Paul Rombouts + + * src/dns_answer.c + I've noticed that when pdnsd is restarted shortly after it has answered a TCP + query, it is often not able to bind to the TCP socket again, resulting in a + disabled TCP server thread. The solution appears to be to set the SO_REUSEADDR + socket option before binding the socket. This allows you to use the same port even + if it is busy (in the TIME_WAIT state). + I found the code for this in a patch file from an old Debian package. + +2004-05-20 Paul Rombouts + + * src/dns_query.c + Joseph Pecquet has reported that version 1.1.11 does not compile under FreeBSD v4.x + because the macro ENONET is undefined. I've bypassed the problem by surrounding + the case line using this value with conditional preprocessor directives. + +2004-05-08 Paul Rombouts + + * src/rc/Slackware/rc.pdnsd + I've included a Slackware start-up script contributed by Nikola Kotur. + +2004-05-05 Paul Rombouts + + * doc/pdnsd.8 + I'm very grateful to Mahesh T. Pai for contributing a pdnsd man page, + which was still missing up till now. + +2004-04-30 Paul Rombouts + + * src/servers.c,src/dns_query.c + After considering some suggestions made by Juliusz Chroboczek I have made the + following changes: + + - After receiving a reply from a remote server mark the server up and update the + timestamp so that pdnsd doesn't bother testing this server for availability for a + while. + - After detecting an error with an send/recv call that indicates a server is + unavailable, mark a server down so that pdnsd doesn't bother testing this server + for a while. + - After server timeouts, uptests are never performed by a query/answer thread, + because this may delay the sending of an answer to the client. Instead the + timestamp of a server that needs to be tested for availability is set to zero and + a condition signal is sent to alert the server status thread, which will carry out + the test. Unresponsive servers with uptest=ping will not be marked down + immediately any more, but only after the ping test has definitely failed. + + * src/error.c,src/error.h + I've moved most of the code previously contained in the DEBUG_MSG macro to a new + function debug_msg(). + The DEBUG_MSG macro now simply expands to "if(debug_p) debug_msg();". + This should make the executable a little smaller, and be just as fast when + debugging is off. The DEBUG_MSG macro still expands to nothing if pdnsd is built + without debugging support. + +2004-04-28 Paul Rombouts + + * src/dns_query.h,src/dns_query.c + I've tried to simplify the finite state machine used for processing parallel + queries, by merging the "state" and "nstate" variables used by p_exec_query() and + p_query_sm() resp. into one "state" variable. + By introducing an extra field "iolen" to keep track of the number of bytes read + from or written to a socket, I could also reduce the number of states for TCP + queries. The new code has the additional advantage that it can handle situations + that require multiple read() calls to receive a response. + +2004-04-14 Paul Rombouts + + * src/dns_query.c + I've added an extra check comparing the number if poll/select events actually + handled to the return value of poll/select. This should reduce the chance that + pdnsd will get caught in a busy spin due to unknown remaining bugs. An error + message is logged and an error code is returned when this comparison fails. + +2004-04-13 Paul Rombouts + + * src/dns_query.h,src/dns_query.c + I got rid of the event field in the query_stat_t struct. + I think it is redundant, because its value can be quite simply derived from + the nstate field. + +2004-04-12 Paul Rombouts + + * src/dns_query.c + I appears there was flaw in the code for handling a "Not Implemented" response + from a remote server with the RA (recursion available) bit equal to zero. This + could cause pdnsd to get into a busy spin. I traced the flaw back to Thomas + Moestl's code, so it must be in all the versions of pdnsd I know of. In previous + versions of pdnsd the busy spin would eventually time out. Due to some recent + changes the loop would no longer time out, making the bug more noticeable. + With thanks to Nicolas George for reporting the bug. + + I also discovered a closely related flaw that would cause pdnsd to poll() closed + file descriptors. It usually works out OK in practice, but it is definitively not + the correct way to do it. + + Additionally, I discovered some opportunities to save memory, e.g. by replacing + the nsname buffer in the query_stat_t struct by a pointer to an already existing + copy of a name. + +2004-04-10 Paul Rombouts + + * src/cache.c + Nicolas George remarked that he thought it was strange that subdomains of domains + negated with "neg" sections in the config file were not also negated. I thought that + he had a point, and I've implemented a change so that negating example.com will + now also negate www.example.com, xxx.adserver.example.com, etc. + +2004-04-09 Paul Rombouts + + * src/error.c,src/error.h + I noticed that the code for the log_warn() and log_error() functions was almost + identical, even to the point that log_warn() called syslog() with LOG_ERR + priority. I've merged these two functions into one log_message() function. + +2004-04-08 Paul Rombouts + + * src/main.c,src/conf-parser.c + The -4 and -6 command-line options should now work as advertised. + This wasn't entirely trivial. The rule is that options on the command line + override those in the configuration file. The easiest way to implement this is to + process the command-line options after reading the configuration file. But this + doesn't work for the -4 and -6 options, because the run_ipv4 flag determines how + IP addresses in the config file are parsed. I've inserted some extra tests and + warning messages that will hopefully make this setting nearly foolproof. + + I've added two new command-line options, "-a" and "-i ". + With the -a flag pdnsd will try to detect automatically if IPv6 support is + available on a system, and fall back to IPv4 if not. The -a flag can be used + instead of -4 or -6. + + In IPv6 mode, pdnsd will now automatically convert IPv4 addresses to IPv6-mapped + addresses. The -i option can be used to specify a prefix for this mapping. The + default is ::ffff.0.0.0.0 + There is also a corresponding ipv4_6_prefix= option for the config file. + + In IPv4 mode, if IPv6 support is compiled in, pdnsd will now skip IPv6 addresses + in the config file (except for the server_ip and ping_ip options) with a warning + message. This allows you to have mixed sets of IPv4 and IPv6 address in the same + config file, although in IPv4 mode some server sections may become inactive. + + With thanks to Juliusz Chroboczek for suggesting these changes. + +2004-04-07 Paul Rombouts + + * src/cache.c + I've changed some of the cache-flag definitions to make debugging a little simpler. + Unfortunately, this makes the cache files of previous pdnsd versions incompatible + with the new one. I've introduced a cache version identifier to be added at the + beginning of each cache file. This enables pdnsd to recognize and discard + incompatible cache files. + +2004-04-05 Paul Rombouts + + * src/cache.h,src/cache.c + I've changed the way CACHE_LAT (cache latency, normally 120 secs) is used to + determine whether a cache entry has timed out. Instead of simply adding it to the + ttl (time to live), I use CACHE_LAT if the ttl is less then CACHE_LAT, else the + ttl itself, making CACHE_LAT the minimum ammount of time a cache entry stays in + the cache. + +2004-04-02 Paul Rombouts + + * src/dns_query.c + I've introduced a global timeout parameter. This is the minimum period of time + pdnsd will wait after sending the first query to a remote server before giving + up without having received a reply. + The timeout options in the configuration file are now only minimum timeout intervals. + Setting the global timeout option makes it possible to specify quite short timeout + intervals in the server sections. This will have the effect that pdnsd will start + querying additional servers fairly quickly if the first servers are slow to respond + (but will still continue to listen for responses from the first ones). + This may allow pdnsd to get an answer more quickly in certain situations. + + * src/dns_query.c + When receiving a NXDOMAIN (unknown domain) response from a remote name server, + I think it is still useful to process the authority and additional sections, + so that pdnsd can possibly add a SOA record to its own response. + +2004-04-01 Paul Rombouts + + * src/dns_query.c + In p_recursive_query(), I've slightly changed the way pdnsd does parallel + queries. Active queries or not canceled until we have received a useful response + from a remote name server, or all the queries have failed or timed out. + Thus the par_queries parameter is no longer the maximum number of parallel + queries, but rather the increment with which the number of parallel queries is + increased when the previous set has timed out. + In the worst case all the servers in the list of available servers will be queried + simultaneously. We may be wasting more system resources this way, but the advantage + is that we have a greater chance of catching a reply. + After all, if we wait longer anyway, why not for more servers. + +2004-03-31 Paul Rombouts + + * src/dns_answer.c + I've noticed that in compose_answer() that while adding the name in the query + section it was not passed through compress_name(). While it is true that the + first name occurrence cannot be compressed, it is still sensible to process the + query name with compress_name() so that the offset can be stored and provide + additional opportunities for future compressions. + I've tested this with dig and the responses of pdnsd are now usually a little + smaller in size or can hold more information within the 512 byte limit. + +2004-03-30 Paul Rombouts + + * src/cache.c + I've noticed that pdnsd stored rr records (of the same type) in reverse order + in the cache. + Although I don't see anything inherently wrong with that, I think it's neater to + store them in the order they are processed. + +2004-03-29 Paul Rombouts + + * src/cache.c + I've rearranged the order of the arguments of some of the functions in cache.c + to obtain a more consistent calling interface. + + * src/dns_answer.c + I've noticed that pdnsd would only add NS records to an authority section if it could + find such records matching the queried name (or the last CNAME in the answer) exactly. + However, I understand that a server should try to give NS records as close as possible + to the target name in the naming hierarchy. + I also understand that if a domain name is reported as nonexisting, or no record of + the requested type exists, it is customary to provide a SOA record, searching up the + name hierarchy if necessary. + I've tried to implement this in compose_answer(), although with some limitations. + I only look in the cache, I don't search more then three levels up, and stop before + the top level. + +2004-03-28 Paul Rombouts + + * src/cache.c,src/dns_answer.c + There were some issues with add_cache_rr_add(). + + First of all, the way it was used in rr_to_cache() (or rather not used) meant + that if an "off topic" record was added for a name that lacked an entry in the + cache, the rr set would be created with an incorrect serial number (namely zero). + I've rewritten add_cache_rr_add so that it can create new cache entries if necessary. + This simplifies the code in rr_to_cache() and ensures correct serial numbers. + + Secondly, in add_cache_rr_add() the ttl was compared with that of an existing rrset + without adjusting for the min_ttl and max_ttl options. This could lead to all the + previous records being deleted, retaining only the last one. + +2004-03-27 Paul Rombouts + + * src/dns_answer.c + In compose_answer(), if the rd (recursion desired) bit is set in the query + and the response contains a CNAME record (while a different type of record was + requested), always do a recursive query on the CNAME, even if we have already + added a record of the requested type to the response. + Failing to honor the rd bit will cause some resolver libraries to complain, + even if the answer contains a record of the requested type. + + I've slightly changed the calling interfaces of add_to_response() and add_rrset() + to make them more consistent and efficient. + + In add_rrset() I've fixed a memory leak on one of the error paths. + + In add_additional_rr(), the return value of add_rr() was not checked. + If add_rr() fails, it will free *ans, and functions higher up the calling + chain could be referencing freed memory. + + I've fixed a potential referencing of freed memory or double freeing in add_additional_a(). + If a call of add_additional_rr() fails, it will free *ans. + Previously, add_additional_rr() could be called a second time, in which case + the second call would be referencing freed memory or freeing it a second time.. + +2004-03-23 Paul Rombouts + + * configure.in, src/Makefile.in,src/pdnsd-ctl/Makefile.in,src/test/Makefile.in + Frédéric L. W. Meunier has reported that configure --srcdir option (for building + in directory separate from the source directory) was broken. + Should be fixed now. + +2004-03-20 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/helpers.c,src/icmp.c,src/main.c,src/netdev.c,src/ipvers.h,src/test/if_up.c,src/test/is_local_addr.c,src/test/tping.c,src/test/random.c,src/conf-parser.c + I've eliminated the global variable run_ipv6 from the code. + Enabling both the IPv4 and IPv6 protocols at the same time is not supported + in pdnsd, so the value of run_ipv6 (if it is defined) is simply !run_ipv4. + + * src/dns.c,src/test/is_local_addr.c,src/test/tping.c + It appears the option to compile pdnsd without IPv4 support (i.e. only IPv6 + support) was broken. Should be fixed now. + +2004-03-19 Paul Rombouts + + * src/cache.c + I've discovered an incorrect use of cache locks in lookup_cache(). + We only read locks in place, it is possible for purge_cent() to delete a cache + entry while another thread is trying to read it at the same time, which could + lead to trouble. I've rewritten purge_cent() so that it can be used to test + whether something needs to be purged without actually deleting anything. + If something needs to be deleted, purge_cent() will be called again with + the proper read/write locks in place, excluding access to the cache for all + other threads. + +2004-03-18 Paul Rombouts + + * src/cache.c + I've added a new function sort_rrl() for sorting the rr_l list using a merge-sort + algorithm. Usually the insertion sort used by insert_rrl() is good enough, because + new entries belong near the end most of the time. Reading entries from disk forms + an exception, though, because the rrsets in the file are completely out of order + w.r.t. timestamps, leading to quadratic time complexity of the insertion sort method. + In that case it should be faster to simply append items at the end of the rr_l list + and sort using a more efficient algorithm afterwords. + pdnsd now seems to start up noticeably faster when reading large cache files. + I've also considered using a more sophisticated data structure than a doubly linked + list, but this will add considerable complexity to the code and use more memory. + +2004-03-13 Paul Rombouts + + * src/dns_answer.c + Changed a declaration in udp_answer_thread() so that the buffer used for passing + control messages on to sendmsg() is exactly the right size, instead of an arbitrary + 512 bytes. + Also initialized the msg_flags of the struct msghdr passed on to sendmsg() to zero, + to keep Valgrind from complaining about uninitialized bytes. + +2004-03-12 Paul Rombouts + + * src/icmp.c + Fixed an incorrect call to select() in ping4(). A file descriptor set for detecting + exceptions was initialized but not passed on to select(). This would lead subsequent + code always to behave as if an IO exception had occurred. + Valgrind seems to indicate that when a poll() call times out and returns 0, + the revents field of the struct pollfd is not necessarily set. + I've changed the code to check that the return value is > 0 before examining the + revents field. + +2004-02-06 Paul Rombouts + + * src/conf-parser.c,src/conf-parser.h,src/conf-keywords.h + I've rewritten the parser for the configuration file in C from scratch. + (f)lex and yacc/bison are no longer needed to build pdnsd. + +2004-01-16 Paul Rombouts + + * src/main.c + Load the cache from disk without locking cache access because pdnsd + is still single-threaded at that point. + +2004-01-15 Paul Rombouts + + * src/cache.c,src/hash.c + Moved the responsibility for freeing the cache entries referred by + the hash buckets from destroy_cache() to free_dns_hash() (which is called + by destroy_cache()). Previously, the cache and hash tables were already + completely destroyed by the time free_dns_hash() was called, and there was + nothing left for free_dns_hash() to free. + +2004-01-14 Paul Rombouts + + * src/hash.c,src/make_hashconvtable.c + The hash conversion table is now generated at build time instead + of at run time when pdnsd is started up. + +2004-01-13 Paul Rombouts + + * src/dns.c + In add_host() fixed incorrect generation of IPV6 type of name for PTR record + due to use of && instead of & as masking operator. + +2004-01-13 Paul Rombouts + + * src/icmp.c, src/dns_answer.c + Use unsigned long instead of int error counters to reduce the danger + of wraparound. + +2004-01-06 Paul Rombouts + + * src/main.c,src/thread.c,src/thread.h,src/server.c,src/status.c,src/dns_answer.c + Initialize a global thread attribute object in main.c and use it to create all the detached + threads, instead of initializing a separate attribute object for each new thread. + +2004-01-06 Paul Rombouts + + * src/dns_answer.c + Check the return value of pthread_create() in udp_server_thread() + and tcp_server_thread() to ensure that a new answer thread has actually + been created and free resources if not. + +2004-01-04 Paul Rombouts + + * src/helpers.c,src/cache.c,src/conff.c,src/status.c + Stop writing to control socket after an error has been detected. + +2004-01-03 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Tried to make the error messages of pdnsd-ctl more helpful. + The complete usage description is now only printed if the 'help' command + is used. For problems with other commands a much shorter message is generated + specific for that command. + +2004-01-02 Paul Rombouts + + * src/helpers.h + Changed the definition of rhnlen(). For valid data this will make no difference, + but it may change the behaviour of pdnsd in certain error situations. + +2004-01-02 Paul Rombouts + + * src/dns.c + Optimized compress_name() some more. + +2004-01-02 Paul Rombouts + + * src/dns_answer.c + Additional code cleanup in compose_answer(). + +2004-01-01 Paul Rombouts + + * doc/pdnsd-ctl.8 + Updated the pdnsd-ctl man page. + +2003-12-31 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Cleaned up some code. + +2003-12-31 Paul Rombouts + + * src/status.c,src/conff.h,src/conff.c + Some further code cleanup in status.c. + Labels for server sections are no longer limited to 32 chars, + but can have arbitrary length. The string that is used to specify + new DNS-addresses with the "pdnsd-ctl server" command can now also + have arbitrary length. + +2003-12-30 Paul Rombouts + + * doc/html/doc.html + Added information about CNAME and MX resource records, that were + previously undocumented. + +2003-12-26 Paul Rombouts + + * src/dns_query.c + Removed the function p_dns_resolve_from(). This function was essentially + a call to p_recursive_query() with a dummy nocache argument. + p_recursive_query() can now be called with nocache=NULL instead. + +2003-12-26 Paul Rombouts + + * src/dns_query.c + Using a variable length array instead of an malloced buffer to hold the struct pollfd array + in p_recursive_query(). This has the potential for causing portability problems, but I + think that's unlikely because almost all the major C compilers I work with support variable + length arrays nowadays. + +2003-10-18 Paul Rombouts + + * src/helpers.h,src/helpers.c + Fixed a mistake that caused a compile error when using the --with-random-device + configuration option. + Thanks to Daniel Black for reporting this bug. + +2003-10-02 Paul Rombouts + + * conf-lex.l.in,src/conf-parse.y,src/conff.h,src/conff.c,src/dns_query.c + Made the "delegation_only" feature configurable. + +2003-09-25 Paul Rombouts + + * src/helpers.c,src/helpers.h + Added alternative implementations of strdup, strndup, stpcpy, getline and asprintf + in an effort to make the code more portable. + +2003-09-22 Paul Rombouts + + * src/helpers.c,src/conf-parse.y + Made some changes to the parser of the configuration file so that domain names + missing a dot at the end will be tolerated. + +2003-09-21 Paul Rombouts + + * src/dns_query.c + Implemented a first version of the "delegation-only" feature. + It has been "hard-coded" to work for "com" and "net" zones, + and is not yet configurable. + +2003-09-21 Paul Rombouts + + * src/dns.c + Rewrote domain_match(). Also changed the way it is used. + I believe it has a cleaner semantics now. + +2003-09-21 Paul Rombouts + + * src/dns_query.c + Changed the order of the arguments of p_exec_query() and p_recursive_query() + to make it more consistent with the other functions. + +2003-09-18 Paul Rombouts + + * src/dns_answer.c + Reordered the code in process_query() so that a buffer for an error response is + allocated only when it is actually needed. + +2003-09-17 Paul Rombouts + + * src/cache.c + Added parentheses to correct mistaken operator precedence assumption in cache.c. + !cent->flags&DF_NEGATIVE is parsed as (!(cent->flags))&DF_NEGATIVE but I think + what Thomas Moestl must have intended was !((cent->flags)&DF_NEGATIVE). + +2003-09-12 Paul Rombouts + + * src/dns_query.c + Fixed a mistake which caused the effect of the proxy_only option to be reversed. + Thanks to Andrew M. Bishop for reporting this bug. + +2003-09-11 Paul Rombouts + + * src/helpers.c + Rewrote str2rhn() and rhn2str(). + +2003-09-10 Paul Rombouts + + * src/dns.c + Rewrote read_hosts(), the function that reads /etc/hosts-style input. + I believe the parsing algorithm is more robust now. + +2003-09-09 Paul Rombouts + + * src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + Fixed a bug (my fault) that caused improper passing on of flags for the + pdnsd-ctl source command. + Also reordered some of the code, so that data is validated after all of it + has been read from the control socket. This should prevent a "broken pipe" + error message if data validation fails. + Also fixed the reporting of success or failure of the pdnsd-ctl "neg" command. + +2003-09-08 Paul Rombouts + + * src/list.c + Rewrote da_grow1() and da_resize() so that they automatically allocate an array + if given a NULL argument. This makes the use of da_create() redundant in most cases. + +2003-09-08 Paul Rombouts + + * src/conf-parse.y,src/servers.c + At the suggestion of Greg Norris, I changed the code to allow server sections in the + configuration file that don't specify any IP addresses. Such a section will remain + inactive until one or more IP addresses are assigned with the control utility pdnsd-ctl. + +2003-09-04 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c + Oops: in my zeal to declare variables in the smallest possible scope, I ended up + using a pointer to a struct that was out of scope. My understanding of compilers tells me + it should work out OK in practice, but it is definitely a no-no. + Used a union declared in a larger scope instead (which is ugly in another way, + but equally efficient). + Also removed a section of redundant code in udp_server_thread(). + +2003-09-01 Paul Rombouts + + * src/dns_query.c + Corrected the iteration range of a for loop in p_dns_cached_resolve(), which would + otherwise cause an array to be indexed out of bounds in the function set_flags_ttl(). + +2003-08-31 Paul Rombouts + + * src/dns_answer.c + Added cleanup handlers for freeing the resources passed on to udp_answer_thread() and + tcp_answer_thread(). This should ensure the resources are freed even if the threads get + canceled. + +2003-08-30 Paul Rombouts + + * src/cache.c + Revised large portions of code in src/cache.c, used for adding and deleting entries in + the cache. In particular, I rewrote purge_cache(), which I believe was incorrect. + I wouldn't be surprised if this was the cause of the crashed (defunct) threads that some + people were reporting. + Also fixed some memory leaks. + +2003-08-28 Paul Rombouts + + * src/cache.c + Eliminated the overhead of allocation debugging in the case that ALLOC_DEBUG is not defined. + +2003-08-24 Paul Rombouts + + * src/conf-parse.y + No longer allow certain settings of the query_method option in the configuration file + if pdnsd is not compiled with the necessary support. + Thanks to Nikolaus Rath for reporting the bug. + +2003-08-23 Paul Rombouts + + * src/netdev.c + Fixed a bug in is_local_addr() where the result of fgetc(f) is restricted to type char + before being compared to EOF, which can result in the comparison always being false. + Thanks to Gerhard Tonn for reporting the bug. + +2003-07-28 Paul Rombouts + + * doc/html/index.html,doc/html/doc.html,doc/html/dl.html,doc/pdnsd-ctl.8,contrib/README + Revised the documentation. + +2003-07-21 Paul Rombouts + + * src/main.c,src/status.c,src/icmp.c + Setting stat_pipe=0 after opening or binding the control socket fails. + This should prevent further use of the control socket if a problem with + it has been detected previously. + Also properly initialized the global variable int ping_isocket in src/icmp.c + +2003-07-13 Paul Rombouts + + * src/main.c + Polished the code in main(). + +2003-07-04 Paul Rombouts + + * src/helpers.c,src/dns_answer.c,src/dns_query.c + Eliminated the use of inet_ntoa() in favor of the more modern inet_ntop(). + inet_ntop() makes more sense in threaded code and is also recommended in + the glibc info pages. + +2003-07-03 Paul Rombouts + + * src/dns_query.c + Fixed an allocation size error (not mine) in p_exec_query(). + The erroneous size is almost always larger than necessary, so in practice this bug + just wastes memory. But there is also a possibility that the allocated buffer is too + small, which would mean trouble. + Also fixed two memory leaks on some of the error paths in p_exec_query(). + +2003-06-28 Paul Rombouts + + * acconfig.h,configure.in,src/thread.h + Extended the configuration option --with-thread-lib. + Configuring with --with-thread-lib=linuxthreads2 will cause the alternative + definition of THREAD_SIGINIT suggested by Thomas Moestl to be used. + +2003-06-27 Paul Rombouts + + * src/consts.h,src/consts.c,src/conff.c,src/conf-parse.y,src/dns_answer.c + Added two new configuration options for policies of inclusion/exclusion lists. + The new policies options are "simple_only" and "fqdn_only". + This allows me to control to which name servers pdnsd will direct queries for + simple host names. + I also polished the code a bit in report_conf_stat(), used for reporting the current configuration. + +2003-06-20 Paul Rombouts + + * acconfig.h,configure.in,src/thread.h,src/thread.c + Added a configuration option --with-thread-lib=nptl. + This causes the macro THREAD_SIGINIT to be defined as empty in src/thread.h, + and thread_sig() in src/thread.c is never used. + +2003-06-11 Paul Rombouts + + * src/thread.h + Undid the change to the definition of THREAD_SIGINIT suggested to me by + Thomas Moestl, after receiving a report of a problem with this change + from someone running SuSE 7.0. + +2003-06-06 Paul Rombouts + + * src/dns_query.c: + Discovered that I failed to preserve the semantics of Thomas Moestl's code + when I rewrote a section of code in use_server(). Fixed. + +2003-05-19 Paul Rombouts + + * src/cache.c,src/conf-lex.l.in,src/conf-parse.y,src/conff.h,src/dns_answer.c,src/dns_query.c,src/servers.c: + Merged fixes contained in patch file sent to me by Thomas Moestl with my own version. + Changing the version to 1.1.8b1 as suggested by the patch file. + +2003-02-26 Paul Rombouts + + * pdnsd-1.1.7a-par.diff: + Made one big patch file from all the changes I made up till now. + Wrote a description of the changes in a file README.par + Posted patch file on the web so others can use it. + +2003-02-24 Paul Rombouts + + * src/cache.c + Changed the code that writes the cache to disk. + Data is now written strictly sequentially, eliminating the need for fseek(). + This seems to have successfully solved the problem I had with corrupt cache files. + +2002-05-27 Paul Rombouts + + * ChangeLog: + Started experimenting with the source code. + Made many changes between 2002-05-27 and 2002-07-13. + Too lazy to maintain the ChangeLog. + +2002-01-06 Thomas Moestl + + * version: Call it 1.1.7. + +2002-01-04 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c: + Comment and debug message fixes, more assertions. + +2002-01-03 Thomas Moestl + + * src/dns.c, src/dns_answer.c, src/dns_query.c: + More harmless fixes, correct some comments and debug messages, add more + assertions. + + * NEWS, version: 1.1.7p2, correct NEWS entry. + + * src/helpers.c: + Make sure the calling thread of pdnsd_exit() terminates immediately. + +2002-01-02 Thomas Moestl + + * src/dns_answer.c, src/helpers.c, src/icmp.c: + Fix a few more harmless bugs, more paranoia. + + * src/status.c: Fix yet more, probably harmless, problems. + +2002-01-01 Thomas Moestl + + * src/dns.h, src/dns_answer.c, src/dns_query.c: + Fix a few more possible buffer size problems, and add a bunch of + assertions as last lines of defence. + +2001-12-30 Thomas Moestl + + * src/dns.c: Build fix (include error.h). + + * NEWS, version: Call it 1.1.7p1, and add a NEWS entry. + + * TODO: Reduce TODO to what actually is still needed. + + * src/dns.c, src/error.h, src/helpers.c: + Add a bunch of robustness PDNSD_ASSERT()'s. + + * src/dns_query.c: + Fix a bug which may possibly be remotely exploitable to gain access as + the user pdnsd runs as. + This was caused by a dumb single-character mistake :( + + * doc/Makefile.am, configure.in: + Avoid confusing automake 1.5 by not putting a comment into a make rule. + Fix CONFDIR passing. + + Submitted by: GoTaR + + * src/pdnsd-ctl/pdnsd-ctl.c: + Avoid crashing when the buffer contents received using the status command + are not terminated. + +2001-10-14 Thomas Moestl + + * src/rc/SuSE/pdnsd.in, THANKS: + Fix the stop case for the SuSE rc script: killproc requires the full + path of the binary as argument (reported by Bernhard Pelz). + +2001-09-23 Thomas Moestl + + * configure.in: + Revamp the OS autodetect test. OpenBSD and (hopefully) NetBSD are no longer + unsupported. + + * src/helpers.c, THANKS: + Do not try to use arc4random when compiling for NetBSD (submitted by + Thomas Stromberg). + +2001-09-10 Thomas Moestl + + * COPYING.BSD: s/REGENTS/AUTHOR/ in one place. + + * src/cache.c: + It is possible no record of the requested type is present after calling + cr_add_cent_rr_int() (when the record was marked as being local), so + check before dereferencing the pointer to the respective rrset. + Leave the record unmodified when cr_check_add() returns 0.x + +2001-07-26 Thomas Moestl + + * src/rc/RedHat/pdnsd.in: + Add a workaround for @sysconfdir@ substitutions containing ${prefix}. + Spotted by Robert Linden. + +2001-07-04 Thomas Moestl + + * src/rc/RedHat/pdnsd.in: + Add a condrestart handler to the Red Hat rc script, and do some general + cleanup. Contributed by Christian Engstler. + +2001-07-02 Thomas Moestl + + * src/error.h: + Attempt to detect a gcc that cannot yet handle ANSI variadic macros, + and work around this by using the old GCC-style variant. + + * src/conff.c: + Remove a + at the start of a line that got in when merging a diff by + hand. + + * src/servers.c: waitpid() returns a pid_t. + + * src/dns.c: + It's sizeof, not sizof. This should unbreak the IPv6 build. Also silence + some warnings with appropriate casts. + + * NEWS, version: Call it 1.1.6, and add a NEWS entry. + +2001-07-01 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_query.c, THANKS, version, AUTHORS: + Added a modified version of Andreas Steinmetz's code for + query_port_start and query_port_range, and added him to AUTHORS and + THANKS. + +2001-06-23 Thomas Moestl + + * src/cache.c: + Fix a bogon: deleted would not be reset correctly in the first + purge_cache loop, which could cause pdnsd to loop forever when a + negative record was after a deleted rr. + +2001-06-21 Thomas Moestl + + * src/list.h: + Add (currently unused) list macros that are going to be used in future + code. + + * src/cache.c: + Fix a bogon in the rw lock code: we need to wake up a writer if there + are no readers. The old code was a leftover from a time when + SUSP_THRESH was just r_pend * x. + Fix a typo. + +2001-06-13 Thomas Moestl + + * AUTHORS: Add mention of FreeBSD code to AUTHORS. + + * src/netdev.c: + Add SIZEOF_ADDR_IFREQ (taken from FreeBSD: _SIZEOF_ADDR_IFREQ, net/if.h + rev. 1.58.2.1) and add an appropriate copyright notice. + The reason for this is that other BSDs don't have it, and we are not + supposed to use underscored macros in portable software. + +2001-06-12 Thomas Moestl + + * src/icmp.c: Fix double #inclusion of . + Noticed by Sebastian Stark. + +2001-06-08 Thomas Moestl + + * src/dns_query.c, THANKS: + Allow underscores in the query names reported back, as the comment next + to the decompress_name call already indicated (but the call gave NULL + as the uscore parameter, which disables underscores normally). + Add Michael Ströder, who spotted this, to THANKS. + +2001-06-06 Thomas Moestl + + * src/servers.c, THANKS: + Fix a bug discovered by Stefan Erhardt (and add him to THANKS): the + return value of waitpid was misinterpreted. + +2001-06-04 Thomas Moestl + + * Makefile.am, file-list.base.in, version: + Bump version to 1.1.6p1; wire up COPYING.BSD so that it gets included + in RPM's and tarballs. + + * COPYING.BSD: + Add the BSD-Style copyright notice so that it can be included in binary + distributions. + +2001-06-03 Thomas Moestl + + * src/dns.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/status.c, NEWS, version: + Bump version to 1.1.5, and add a NEWS entry for this release. + + Miscellaneous cleanups, mainly in the status.c code; fix a bug that + could cause heap corruption (rhncpy always clobbered the whole buffer, + but only the needed space was reserved in add_rr). This should solve + the crashes some people were seeing (this bug is not an exploitable + security hole as far as I know; the respective buffer is on the heap, + as mentioned). + + * src/error.c: + Paranoia: do not use the argument to crash_msg as a format string + (crash_msg is only used with constant strings, though). + +2001-06-02 Thomas Moestl + + * src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/error.h, src/hash.c, src/helpers.c: + Correct underscore handling for SRV records, and a few comment fixes. + + * src/cache.c, src/conff.c, src/dns_query.c, src/error.h, src/helpers.h, src/status.c: + Numerous non-critical argument fixes for printf-like functions. + + * src/dns.c: Remove superfluous \n's. + + * src/conf-parse.y, src/dns_answer.c, src/status.c: + Correct some DEBUG_MSG nits, and fix two format string bugs. One of + them could allow users that are allowed to use pdnsd-ctl with the + server (when the status socket is enabled) to gain the privileges of + the user that runs (the run_as user or the user that started pdnsd on + Linux when strict_setuid is set to off) pdnsd. The status socket is + disabled by default, and if it is enabled, it's default permissions + are quite restrictive, so this isn't a problem for most. + +2001-05-30 Thomas Moestl + + * src/status.c: + Make the status permissions actually work (missed last time). + + * src/dns_answer.c, src/main.c, src/status.c, src/status.h: + Move the status socket initialization to a place where it gets executed + before any threads are started; this way, we can use umask to set the + permissions, and avoid a (in this case harmless, but anyway) race + condition. + While being there, remove obsoleted comments and places referring to + the now-socket as fifo. + +2001-05-29 Thomas Moestl + + * src/cache.c: + Replace a misuse of CF_LOCAL with DF_LOCAL. This had no effect, because + the values are the same. + +2001-05-22 Thomas Moestl + + * src/hash.c, src/helpers.c, THANKS, acconfig.h, configure.in: + Add an option for allowing underscores (_) in domain names. This + violates the RFC's if enabled (which it isn't by default). + Thanks to Eelco Vriezekolk for an initial patch. + + While being there, clean up configure.in and acconfig.c a bit. + + * src/helpers.c, src/status.c: + Add a few comments about security implications. + + * src/cache.c, src/dns_answer.c, src/dns_query.c, src/helpers.c: + Change some occurences of strcpy to strncpy. Again, no risk here, the + buffer lentgh was carefully chosen, and while the data was partially of + remote origin, it was carefully validated before entering the cache (and + thus having a chance of being used by us). + 3 occurences remain: 2 in cache.c, where we allocate a sufficient amount + of memory before (mimicking the non-portable strdup) and one where + we copy a constant and which is obviously correct. + + * src/dns.c: + Change two occurences of strcat to strncat. Again, no risk here, the + buffer lentgh was carefully chosen, the data was validated and supplied + by the starting user. + + * src/dns.c: + Change a sprintf to a snprintf and enlarge a buffer a bit. This is pure + paranoia (alrhough makes code review easier for others), because a.) the + lengths were carefully chosen so that no overrun could occur and + b.) this was locally supplied data. + +2001-05-21 Thomas Moestl + + * src/rc/RedHat/Makefile.am: + Add a missing semicolon in the RedHat rc Makefile.am (discovered by + Christian Engstler). + +2001-05-19 Thomas Moestl + + * HACKING: Remove the mostly outdated HACKING file. + + * src/debug.c: + Catch a corner case in the allocation debug helpers: realloc() with a + size of 0 is effectively a free operation. To my knowledge, this is + not done in the pdnsd sources, however. + + * src/test/test.sh: + Use the correct error function, forgotten in last commit. + + * src/test/clnt-test.sh, src/test/srv-test.sh, src/test/test.sh: + Misc small improvements in the regression test scripts, mostly + adding configuration variables and common error handlers. + Comment a little on the tests that are done in clnt-test.sh + + * src/rr_types.c: Fix a typo in a comment. + + * src/cache.c, src/debug.c, src/dns_answer.c, src/dns_query.c, src/error.h, src/hash.c, src/list.c, src/main.c, src/status.c, src/thread.c, src/thread.h: + New ANSI variadic debug macros (finally), which print a timestamp and + a thread ID now for easier debugging with many parallel queries. + It should be considered to make those inline functions instead. However, + we have the advantage that we use printf in place here and benefit + from parameter checking without specifying obscure function attributes. + + * src/rc/SuSE/.cvsignore, src/test/.cvsignore, src/rc/.cvsignore, src/rc/Debian/.cvsignore, src/rc/RedHat/.cvsignore, contrib/.cvsignore, doc/.cvsignore, src/.cvsignore, src/pdnsd-ctl/.cvsignore, .cvsignore: + Brush up the rotten (pre-autoconf!) .cvsignore files and add some where + necessary. + +2001-05-17 Thomas Moestl + + * src/rc/RedHat/Makefile.am, src/rc/RedHat/pdnsd.in, configure.in, pdnsd.spec.in: + Red Hat rc script and RPM improvements by Christian Engstler. + +2001-05-12 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c: + Fix a place missed when converting rr_info. + + * version: It's 1.1.4, finally. + + * src/rr_types.c, version: + Change some class values in the rr type structure to better values. + Bump beta version. + +2001-05-10 Thomas Moestl + + * src/cache.c, src/dns_answer.c, src/dns_query.c: + Fix a signedness bug that could cause erraneous 0 ttls to be returned. + Add some debug messages, and do some minor fixups. + +2001-05-09 Thomas Moestl + + * src/cache.c, src/rr_types.c: + Fix some bugs in the new conflict resolution code and make it more + complete. + + * src/cache.c, version: + Add conflict resolution code. This needs a bit more checking, and + the tables might still need to be tweaked. + Bump version. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/Makefile.am, src/cache.c, src/cache.h, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.c, src/consts.h, src/debug.c, src/dns.c, src/dns_answer.c, src/dns_answer.h, src/dns_query.c, src/dns_query.h, src/error.c, src/error.h, src/hash.c, src/hash.h, src/helpers.c, src/helpers.h, src/icmp.c, src/icmp.h, src/list.c, src/main.c, src/netdev.c, src/netdev.h, src/rr_types.c, src/rr_types.h, src/servers.c, src/servers.h, src/status.c, src/status.h, src/thread.c, src/thread.h, configure.in, version: + Remove the old infrastructure that theoretically could have allowed for + multiple cache subsystems. This ability was never used, and if it should, + the caching should probably be split into two layers, a higher level + common one and the actual caching backends. + src/cacheing/cache.c and src/cacheing/native/*.[ch] were repo-copied to + src/. + Substitute "conf.h" with for includes. + Purge records a little more often (when adding records, and when + retrieving from the cache). Handle cache_size properly when using + purge_cent. + Introduce some infrastructure in rr_types.[ch] for a record conflict + checker which is to be introduced shortly to enforce cache consistency + even in the purge_cache=off case. + +2001-05-04 Thomas Moestl + + * src/rc/RedHat/pdnsd.in, src/rc/SuSE/pdnsd.in, src/rc/Debian/pdnsd.in: + Revert the last commit. It breaks the rc scripts by spamming them with + make style variable expansions. + + * src/rc/SuSE/pdnsd.in, src/rc/RedHat/pdnsd.in, src/rc/Debian/pdnsd.in, AUTHORS, THANKS: + Fix a rc script bug spotted by Frank Elsner, and add him to AUTHORS and + THANKS. + +2001-05-01 Thomas Moestl + + * version: Bump version to 1.1.4p2. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c: + Fix some bogons and remove some unneeded code in the pdnsd-ctl + interface. + Fix spelling and line length bugs. + +2001-04-30 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/conf-parse.y, src/status.c: + Some corrections for the authrec config file and the pdnsd-ctl noauth + support. + + * src/pdnsd-ctl/pdnsd-ctl.c: + Fix wrong argv index (using getopt changed the indices). + + * src/pdnsd-ctl/pdnsd-ctl.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_query.c, src/status.c, THANKS, version, AUTHORS: + Accumulated changes that should go in before 1.1.4: + - merge Andrew M. Bishop's patch that adds a server label option + - make local records authoritative for the domain by default, and add + the authrec option to change this + - add the auth keyword to the pdnsd-ctl source option to support that + - fix a bug in the conf-parse.y grammar causing a shift/reduce conflict + - sync up AUTHORS and THANKS: add Andrew M. Bishop, Kevin A. Burton and + Michael Steinl + - bump version to 1.1.4p1 + + * src/conff.c, src/main.c: + Fix two small bugs: the wrong element of argv was used for the pidfile + option, which could cause pdnsd to segfault, and C_INCLUDED was always + used in slist_add, regardless of the tp parameter. + + * src/helpers.c: + Fix a bogon discovered by Michael Steiner: the fread() return value + was tested against bytes, not the number of items. + + * src/hash.c, src/hash.h, src/cache.c: + purge_cache used to walk over the cache quite inefficiently when it was + called from add_cache. Add a lazy mode for purge_cache which uses the + rrset_l to be efficient in this special case. + Add some #ifdef'ed-out-by-default code to debug the hash function. + +2001-04-12 Thomas Moestl + + * NEWS: Add NEWS entry for 1.1.3. + + * src/dns.c, src/helpers.c, src/icmp.c, contrib/Makefile.am, contrib/README, version: + IPv6, ICMP and build fixes. It's 1.1.3 now! + + * src/debug.c, src/debug.h: Add the new debug support files. + + * src/test/clnt-test.sh, src/cache.c, src/cache.h, src/error.h, src/list.c, src/list.h, src/main.c, src/status.c, src/status.h, src/Makefile.am, src/conf-parse.y, src/conff.h, src/dns.c, src/dns_answer.c, src/dns_query.c: + Add allocation debug support. Some small cleanups before the upcoming + 1.1.3 release. + + * src/dns_query.h, src/helpers.c, src/list.c, src/conff.c, src/dns.c, src/dns_answer.c, src/dns_query.c: + Lots of small bugfixes, cleanups, style and spelling fixes. + + * src/test/clnt-test.sh: Fix nc arguments. + + * src/test/clnt-test.sh, src/test/srv-test.sh, src/test/test.sh: + Add regression test scripts. + +2001-04-11 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/servers.c, src/dns_answer.c, src/helpers.c, src/helpers.h, src/icmp.c, src/main.c: + Further cleanups and bug, style and spelling fixes. + + * configure.in: Use -g again in the CFLAGS for a while. + + * version: Beta version bump. + + * src/rc/SuSE/pdnsd.in: + killproc does not seem to take the full path, but only the process name + (which is what one would expect). + + * src/hash.c, src/netdev.c, src/rr_types.c, src/status.c, src/conf-parse.y, src/conff.c, src/helpers.c: + Misc. smaller fixes, and fixes on the new features. Also clean up style + and spelling in some places. + + * src/dns_answer.c: + Bring the glibc pthread_cleanup_push/pthread_cleanup_pop return bug + workaround into the main tree. + Without this, a return between those two macros would cause pdnsd + to crash on system using a glibc between 2.1.2 and 2.2.2 (and possibly + others). This could e.g. be cause by a TCP connect() port scan. + +2001-04-10 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c: + Minor fixes, direct error messages to stderr. + + * src/list.c, src/list.h: Add the new list implementations. + + * src/cache.c, src/cache.h, src/conf-parse.y, src/dns.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/helpers.h, src/conf-lex.l.in: + Introduce rhnlen and rhncpy and make use of it instead of kluged-up + strcpy/strlen in the appropriate places. + Check that incoming names contain only legal characters in + decompress_name, return RC_FORMAT otherwise (this would result in + wrong handling only, but not in a security hole). + Reorganzie compose_answer and make it more correct for multiple + questions. Get rid of the algorithm that tries to add a higher + level name server; this might be readded in another place somewhen. + Use some more da_* instead of hand-built lists. + Some style cleanups. + + * src/rc/RedHat/Makefile.am: + Add K45pdnsd links for rc6.d (reboot) and rc0.d (halt) following a + suggestion by Stas Sergeev. + +2001-04-06 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.h, src/error.h, src/helpers.c, src/helpers.h, src/main.c, src/servers.c, src/status.c, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/Makefile.am, version: + Bump alpha version; introduce a generic dynamic array type and make use + of it to ged rid of some ugly casts and redundant code. + Minor fixes. + + * src/icmp.h, src/ipvers.h, src/conff.h, src/consts.h, src/dns.h, src/dns_answer.h, src/dns_query.h, src/helpers.h: + Use macros without an underscore as first character to protect the + headers. Underscores are reserved and should not be used in the + application name space. + + * src/error.h: Add PDNSD_ASSERT, change style a little. + +2001-04-03 Thomas Moestl + + * src/hash.c, src/netdev.c, src/servers.c, src/helpers.c, src/icmp.c, src/main.c: + Another slew of small bugfixes, minor updates and small fixes. + + * src/rr_types.c, src/consts.c: + Update rr_types.c copyright date, consts.c should have a rcsid string. + + * src/rr_types.c: cvs add rr_types.c. + + * src/dns.h, src/helpers.h, src/ipvers.h, src/rr_types.h, src/status.h, src/conf-parse.y, src/conff.h: + cvs add rr_types.h that got missed before, update copyright dates, + remove some old config cruft, some minor fixups. + + * src/conff.c, src/consts.c, src/dns_answer.c, src/error.c, src/conf-lex.l.in, src/conf-parse.y: + Update copyright dates, fix some minor bugs. Update copyright dates. + cvs add missed consts.c. + +2001-03-28 Thomas Moestl + + * src/cache.c, src/hash.c, src/error.c, src/servers.c, src/dns_answer.c, version: + Bump version to 1.1.3p4 + Fix some non-critical locking issues (none of them could be fatal). + Adjust copyright dates. + + * src/hash.c: Make the hash compare case insensitive. + +2001-03-25 Thomas Moestl + + * contrib/Makefile.am: Add Id tag to Makefile.am + + * src/cache.c, src/dns_answer.c, src/icmp.c: + Some more type fixes overlooked in last commit. + + * src/conf-parse.y, src/conff.h, src/dns.h, src/dns_answer.c, src/dns_query.c, src/icmp.c: + More type cleanups. Use time_t for time specifications throughout, and + make lengths singed longs. Cast cleanup in icmp.c to fix alpha + unalinged access faults. + + * contrib/dhcp2pdnsd, contrib/pdnsd_dhcp.pl, contrib/save_ram.pl, contrib/Makefile.am, contrib/README, configure.in, version, Makefile.am: + It's 1.1.3p3 now. + Change the contrib infrastructure: there is a Makfile.am in contrib/ + now. Rename Marko Stolle's pdnsd_update.pl to pdnsd_dhcp.pl and bring + it up to date (adding the rc script and save_ram.pl). + + * src/helpers.c, src/dns.c, src/dns_query.c, AUTHORS, THANKS: + Bring in Bjoern Fischer's changes to make pdnsd conserve the case of + cached names, and add him to AUTHORS and THANKS. + + * configure.in: The gdbm backend is discontinued. + + * src/cache.c, src/hash.c: + Cleanup and small bugfixes of the cache code (esp. locking). + + * AUTHORS, THANKS, file-list.base.in, pdnsd.spec.in: + SuSE fixes by Christian Engstler. + Add him to AUTHORS, THANKS. + +2001-03-14 Thomas Moestl + + * src/dns_query.c: + Fix a bug that could cause servers that were not used in the first + parallel query not to be used at all (failure would be returned + instead). + +2001-03-13 Thomas Moestl + + * src/icmp.c: Add define for ip_p equivalent on Linux. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns_query.c, src/icmp.c, src/status.c, src/conf-parse.y, src/dns_answer.c, version: + Bump alpha version, more alignment fixes. All casts should be correct + now. + +2001-03-12 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, AUTHORS, THANKS: + Add the alpha fixes by P.J. Bostley, and add him to THANKS and AUTHORS. + +2001-03-10 Thomas Moestl + + * src/dns.h, src/helpers.h: + Remove prototype for removed function strtolower. + Use unit16_t and uint32_t instead of unsinged short/long for dns + protocol structures. + +2001-02-25 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c, src/conf-lex.l.in, src/conf-parse.y, AUTHORS, Makefile.am, version: + Add MX and CNAME for rr sections in the config file and MX setting + for pdnsd-ctl. + Typo fixes. + + * src/netdev.c: Two more fixes. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns.c, src/main.c: + More small robustness fixes. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/netdev.c, src/status.c, src/status.h, src/conf-parse.y, src/helpers.c, src/main.c, configure.in, version, Makefile.am, NEWS: + A batch of robustness fixes. Move the status socket to the cache + directory. Various cleanups. + It's 1.3 now (hopefully to be released soon). + +2001-02-21 Thomas Moestl + + * src/main.c, src/conf-lex.l.in: + Fix breakage of the -mtu option and the query_method option (the parser + would not recognize constants that contained underscores). + +2001-02-20 Thomas Moestl + + * contrib/README, contrib/pdnsd_dhcp.pl, AUTHORS, Makefile.am, THANKS, file-list.base.in: + Add Marko Stolle's pdnsd_update.pl DHCP update script, add him to THANKS, + and bring a contrib/ directory in place. + +2001-02-15 Thomas Moestl + + * src/dns_answer.c, configure.in, version: + Some minor build & misc fixes. Bump version to 1.1.2a and release a + version with the spec file fixes to get proper Red Hat RPM's. + +2001-02-09 Thomas Moestl + + * NEWS: Bring NEWS up to date. + + * src/icmp.c: Do not close the socket on error. + + * pdnsd.spec.in: Add spec file fixes for man pages by Sourav K. Mandal + +2001-02-07 Thomas Moestl + + * version: It is now 1.1.2. + + * src/dns_query.c, src/main.c, Makefile.am, THANKS: + Fix a too strict length checking that could cause SERVFAIL to be returned + when the server returned NXDOMAIN. Add Markus Storm to THANKS (he has + reported this bug and supplied helpful information). + Minor tweaking in main.c. + Remove emptying of GZIP_ENV in Makefile.am (this normally contains --best). + +2001-01-27 Thomas Moestl + + * AUTHORS, THANKS: + Add Michael Wiedmann to AUTHORS and THANKS for his pdnsd-ctl.8 man page. + + * doc/Makefile.am, doc/pdnsd-ctl.8, configure.in, Makefile.am: + Add the pdnsd-ctl man page contributed by Michael Wiedmann. For this to + build in a correct way, add doc/Makefile.am and move all doc and + pdnsd.conf.sample related stuff in there. + +2001-01-25 Thomas Moestl + + * src/main.c: Removed unneeded for the non-O_NOFOLLOW case. + +2001-01-24 Thomas Moestl + + * src/main.c: + Add a fchown and a fchmod to the new non-O_NOFOLLOW case (not yet used). + + * src/conf-parse.y, src/main.c, src/status.c: Misc small fixups. + + * version: It's called 1.1.1 now. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c, src/main.c: + Fix command line parsing. Add code to securely create pid files under + OSs that do not support the O_NOFOLLOW flag (those OSs are not supported + yet, though). + Fix a possible race condition in socket creation/chmod. We now create + a directory in /tmp (or whatever TEMPDIR was set) to hold the socket. + + * src/dns.c, src/dns_answer.c, src/icmp.c, src/main.c, src/status.c: + Another slew of copyright notice upgrades. + + * version, configure.in: + Bump beta revision, fix typo (missing $) in configure.in + + * src/dns.c, src/dns_answer.c, src/status.c: + Silence BSD compile time warnings. + + * configure.in: + Cleanup, add autoconf code for building pdnsd on FreeBSD-CURRENT with the + new additionally-linked libc_r. + +2001-01-16 Thomas Moestl + + * src/dns_answer.c, src/icmp.c, version: + Bump beta revision, fix a comment. Also, generate ping id's using pdnsd's + random wrappers instead of using rand() for paranoia. + +2001-01-15 Thomas Moestl + + * src/helpers.c, configure.in: Improve wording. + + * src/helpers.c: Update copyright year (forgotten in last commit). + + * src/dns_answer.c, src/helpers.c, src/main.c, acconfig.h, configure.in, version: + Bump versions. Small fixes (move socket intitializations from + udp_server_thread to init_udp_sockets to prevent warning when startup + takes long. + Make arc4random an option for a query id RNG and make it the default + on FreeBSD. + +2000-12-07 Thomas Moestl + + * src/conf-parse.y, src/main.c, version: + We are at 1.1.1p1. Removed the exec-uptest security warning printef if no + explicit user is given in the strict_setuid case (it is not needed there, + and confuses users). + +2000-11-28 Thomas Moestl + + * src/cache.c, src/helpers.c: + Converted cache locks to use condition vars and have lock contention + prevention. Added comments where not converted. + +2000-11-25 Thomas Moestl + + * AUTHORS, THANKS, pdnsd.spec.in: + Added spec file patches by Bernd Leibing and added him to AUTHORS and + THANKS. + +2000-11-21 Thomas Moestl + + * src/rc/SuSE/Makefile.am: Fixed a hopefully last SuSE rpm build bug. + + * src/rc/SuSE/Makefile.am: + Another one: allow rc.conf manipulation to fail for a clean + rpm build (SuSE only). + + * file-list.base.in: + Last-minute fix: correct filelist for rpm build to reflect the new name + for the sample configuration. + + * version: It's 1.1.0 now. + +2000-11-18 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns_query.c, version: + Fixed a condition where the cache code did not give up a lock. + Made the udp code use connect(). + Some small changes. + +2000-11-16 Thomas Moestl + + * version: Calling it 1.1.0b3. + +2000-11-15 Thomas Moestl + + * src/test/Makefile.am, src/pdnsd-ctl/Makefile.am, src/cache.c, src/Makefile.am, src/dns_answer.c, src/error.h, src/icmp.c, src/icmp.h, src/main.c, src/netdev.c, src/servers.c, src/thread.c, Makefile.am, configure.in, version: + Enabled new rr support by default (some resolvers don't seem to like not + supported answers - not our bug, but well). + Made some globals volatile to avoid being bitten by optimisations. + +2000-11-12 Thomas Moestl + + * TODO, version: Called it the first beta. + +2000-11-11 Thomas Moestl + + * src/cache.c, src/conf-lex.l.in, src/conf-parse.y, doc/pdnsd.conf.in: + renanmed rrneg to neg in the config file. + Misc small fixes. + pdnsd-ctl record xxx inval will now also invalidate local records. + + * src/conf-lex.l.in, src/conf-parse.y, src/dns_answer.c, src/status.c, pdnsd.spec.in: + Added --sysconfdir=/etc as argument to configure in the spec file. + Implemented the new rrneg config file section. + + * src/test/Makefile.am, src/pdnsd-ctl/Makefile.am, src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/cache.h, src/status.c, src/status.h, TODO: + Added the neg option to pdnsd-ctl. + + * src/cache.c, src/Makefile.am, src/conf-lex.l.in, src/consts.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/main.c, configure.in: + Assorted fixes. The new features should be stabilized by now, will + integrate the missing few features now. + Also actived the tcp server by default. + +2000-11-07 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, src/icmp.c, src/ipvers.h, THANKS, TODO: + Fixed a possible memory and socket leak reported by Erich Reitz. + Implemented udp source address discovery for FreeBSD. + + * src/dns_query.c: Part 2 of yesterdays fix. + +2000-11-06 Thomas Moestl + + * src/dns_query.c, version: + Fixed a bug reported by Erich Reitz: pdnsd could leak fd's and memory if + queries timed out. + + * src/cache.c, src/cache.h, src/Makefile.am, src/dns_answer.c, src/dns_query.c, TODO, configure.in: + Sorted out some bugs for the new neg cacheing. + +2000-11-05 Thomas Moestl + + * src/cache.c, src/dns_query.c, TODO: + Negative cacheing support is now present, but largely untested. + + * src/conff.c, TODO: + The output of pdnsd-ctl status is now complete with all currently + supported options. + + * src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/conf-lex.l.in: + Added the config file options for the nefative cacheing support. + +2000-11-04 Thomas Moestl + + * src/cache.c, src/cache.h, src/conf-parse.y, src/dns.c, src/dns_query.c, src/status.c, version: + The new cache infrastructure for negative cacheing is in place. + Using and testing it remains. + + * src/cache.c, src/cache.h: + First changes to support negative cacheing. This should not break + anything, but the cache file format will be incompatible. + + * src/main.c, src/dns_answer.c: + init_udp_socket() and init_tcp_socket() are now called after + daemonizing on FreeBSD, as bind wants to lock the fd which + can cause later calls to fail after an exit. + +2000-11-03 Thomas Moestl + + * src/dns_query.c, src/icmp.c, src/servers.c: + Minor bugfixes and repository cleanup. + +2000-11-02 Thomas Moestl + + * src/icmp.c, TODO, version: + Called it 1.0.16p4. Fixed some compatability problems with the new code; + the ipv4 implementation seems to be fairly stable, the ipv6 one needs + some testing with dest unreach messages. + + * src/icmp.c, src/main.c: + Rewrote large parts of the ping implementation to be more portable. + +2000-11-01 Thomas Moestl + + * acconfig.h, configure.in, version: + Some config fixes, version to 1.0.16p3. SOCKET_LOCKING should be + off by default, as sendmsg can block. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/error.h, configure.in: + Removed -W* arguments from CFLAGS that were implied by -Wall. + + * src/thread.c, configure.in: + Added some more safety tests to configure.in, and made it give + an error on some conditions. Also made configure do poll and usleep + detectione. + + * src/cache.h, src/test/Makefile.am, src/dns_answer.c, src/error.c, src/error.h, src/helpers.c, src/main.c, src/servers.c, src/status.c, src/thread.c, src/thread.h, src/Makefile.am: + Code cleanup. Beautified some macros, and moved the thread + specific things from error.[ch] over to the new thread.[ch]. + Also introduced usleep_r which tries to be thread safe for + different Unices. + +2000-10-31 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl, src/pdnsd-ctl/pdnsd-ctl.c: + The pdnsd-ctl binary got into cvs. Fixed that. + + * src/pdnsd-ctl/pdnsd-ctl, src/cache.c, src/cache.h, src/error.c, src/helpers.c, src/main.c, version: + Another set of FreeBSD compatability patches. This seems to catch + most of the problems, and pdnsd should be useable with libc_r now. + +2000-10-30 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl, src/conff.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/icmp.c, src/main.c, AUTHORS, Makefile.am, THANKS, configure.in, version: + FreeBSD fixes, mostly contributed by Roman Shterenzon. + +2000-10-25 Thomas Moestl + + * src/pdnsd-ctl/Makefile.am, src/pdnsd-ctl/pdnsd-ctl: + pdnsd-ctl was not in cvs. + + * src/dns_query.c, src/error.h, configure.in: Some fixups for 1.0.15. + +2000-10-23 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, configure.in: + Several fixes for pdnsd to work better when it receives error replys. + + * src/dns.c, src/dns_answer.c: + Fixed another memory leak on an error path in dns_answer.c and did + a pointer signedness fixup in dns.c + +2000-10-21 Thomas Moestl + + * src/dns_answer.c, AUTHORS: + Applied a patch by Paul Wagland that fixes some spelling mistakes + and some memory leaks on error paths. + + * src/dns_query.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, NEWS, version: + Bug fixes. Added the randomize_recs option and turned it on + by default. + +2000-10-20 Thomas Moestl + + * src/helpers.c, src/dns.c, src/dns_query.c: + Fixes for the paranoid option to work with root servers + properly. + + * src/dns_query.c, src/dns.h, src/dns_answer.c, AUTHORS, THANKS: + Applied a patch by Paul Wagland for bind9-compatability and added + him to AUTHORS and THANKS. + +2000-10-19 Thomas Moestl + + * src/dns_answer.c, NEWS, version: + Another POLL_* fix. It is now called 1.0.14. + + * src/dns_answer.c, src/dns_query.c, src/icmp.c: BSD build fixes. + + * src/dns_query.c: + Made p_recurdive_query return immediately if a query returns + NXDOMAIN. + + * src/dns_query.c, Makefile.am: + Some bugifixes. It is now called 1.0.13. Releasing. + + * src/cache.c, src/dns_query.c, AUTHORS, NEWS: + Updated AUTHORS and NEWS. Made destroy_cache() lock the cache so + that no thread can access the cache afterwards (could lead to + crashes). + + * src/helpers.c, NEWS, THANKS: + Integrated a security fix contributed by Olaf Kirch: when + changing user IDs, pdnsd did not reinitialize the supplementary + group list, meaning that the process still had the privileges + of the supplementary groups the original user was member of. + + * src/conf-lex.l.in, src/conf-parse.y, Makefile.am, TODO, version: + Introduced the par_queries option. + + * src/dns_answer.c, src/dns_query.c, TODO: + Updated TODO, did some fixups for string handling. + + * HACKING: Added HACKING with some comments about coding style. + +2000-10-18 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, src/error.h, src/main.c, TODO: + Revieved and fixed the new dns_query.c-poll/select loops. + + * src/test/tping.c, src/dns_query.c, src/icmp.c: + Fixed the new poll/select ping support. + +2000-10-17 Thomas Moestl + + * src/dns_query.c, src/icmp.c, TODO: + Got rid of the O_NONBLOCK loop in the icmp.c ping implementation. + Beta tesing pending. + + * src/rc/Debian/Makefile.am, src/conff.c, src/conff.h, src/dns_query.c, src/dns_query.h, THANKS, TODO, acconfig.h, configure.in: + Corrected the Debian rc script (bug reported by Michael Müller). + Got rid of the nonblocking socket things in dns_query.c, and + using poll/select now. + Testing (esp. --no-poll) remains. + + * src/dns_answer.c: + Got rid of O_NONBLOCK read loops in dns_answer.c, using poll/select + now instead (after one issue about boundaries was cleared up). + +2000-10-16 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am: + The generated rc scripts do not need to be in the distribution. + + * src/conff.c, src/main.c: + Fixed a server structure members in conff.c. Only delete the socket + if we are in status pipe mode now. + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, src/dns_query.c, acconfig.h, configure.in: + Added the --enable-tcp-subseq and --with-tcp-qtimeout configure + options, added the tcp_qtimeout conf file option, tested things. + 1.0.12 is ready for release. + +2000-10-15 Thomas Moestl + + * src/dns_answer.c, TODO, acconfig.h: + Added TCP timeouts to the answer code. Still need an option in the + conf file and documentation for that (besides beta testing). + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns_query.c, src/helpers.c, src/helpers.h, NEWS, TODO: + Introduced domain inclusion/exclusion lists in the server section + (new options include=, exclude=, policy=). + +2000-10-14 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, src/main.c, NEWS, TODO, version: + Upped version, updated NEWS and TODO and implemented a process + count limit. + +2000-10-13 Thomas Moestl + + * src/rc/Debian/pdnsd.in, src/main.c: + Added the --pdnsd-user option, and made the Debian rc script + use it rather than trying to parse the config file itself. + +2000-10-11 Thomas Moestl + + * src/rc/SuSE/pdnsd, src/rc/SuSE/pdnsd.in, src/rc/RedHat/pdnsd, src/rc/RedHat/pdnsd.in, src/rc/Debian/pdnsd, src/rc/Debian/pdnsd.in, AUTHORS, THANKS, configure.in: + Added the 'configure'-able rc scripts contributed by Carsten Block + and added him to THANKS and AUTHORS. + + * src/main.c: + Added O_NOFOLLOW to the pidfile open() call (if it is defined) + to prevent users creating files as the pdnsd user (using links) + if the admin put the pidfile in a world-writeable directory + against all good advice. + This is not a bug fix! Admins were not, and are still not supposed + to put the pidfile in a directory that is writeable for untrusted + users! + +2000-10-10 Thomas Moestl + + * THANKS: Added Milan P. Stanic to THANKS. + + * src/main.c: + Fixed a missing O_WRONLY in the open() call for pidfile operation. + +2000-10-08 Thomas Moestl + + * src/Makefile.am, src/dns.c, src/dns_answer.c, configure.in, version, acconfig.h: + Released 1.0.11. + Two security fixes in dns.c and dns_answer.c, and misc. smaller issues. + + * src/Makefile.am, src/conf-parse.y, src/dns_answer.c, src/dns_query.c, src/icmp.c, src/servers.c, AUTHORS, THANKS, TODO, acconfig.h, configure.in, version: + 1.0.10 was released some time ago ;-) + This had some IPv6 fixes. + Also fixed minor bug when using SOCKET_LOCKING. + +2000-08-28 Thomas Moestl + + * src/dns_answer.c: Fixed a parameter mismatch in getsockopt() + + * Makefile.am: + Applied Sourav K. Mandal's rpm build patch to the toplevel + Makefile.am + +2000-08-27 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/consts.h, src/servers.c, version: + Added diald support. It's now called 1.0.9. + + * src/conf-parse.y, src/conff.c, src/netdev.c, pdnsd.spec.in: + Fixed some ugly typos in conf-parse.y and netdev.c. + Since I have no further bug reports and these bugs make some + things inconvenient, I will release 1.0.9 immediately. + +2000-08-26 Thomas Moestl + + * pdnsd.spec.in: small spec fix. + + * NEWS, configure.in: Last fixups for 1.0.8. Released it. + + * Makefile.am: Set mode and owner for cache file. + + * src/conf-parse.y, src/dns_answer.c, src/netdev.c, acconfig.h, configure.in, version: + Misc fixes. Hopefully fixed the UDP socket problems under Linux SMP. + +2000-08-20 Thomas Moestl + + * src/cache.c, src/dns_answer.c, AUTHORS, THANKS, configure.in, pdnsd.spec.in, version: + Build fixes by Alexandre Nunes, spec fixes (does now set distro for + configure), first attempt at an "error in udp send"-fix, and fix + for a problem with having records for the root domain in the disk + cache file. + +2000-08-13 Thomas Moestl + + * src/netdev.h, src/servers.c, src/status.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns.c, src/dns.h, src/main.c, src/netdev.c, AUTHORS, THANKS: + Some minor fixes. Integrated the ppp device patch by Ron Yorston. + +2000-08-12 Thomas Moestl + + * configure.in: + configure.in was missing in repository. Also removed debugging + flag for build. + + * src/main.c: Made the pid file handling safe for directories. + + * src/dns.c: Part II of the last fix. + + * src/dns.c: + Fixed a nasty bug in decompress_name which would produce errors very + rarely. That was a off-by-one bug, but on the safe side (no overflow, + stopping one by too early). + + * src/main.c: + Fixed several possible problems with strncat(). None of these was + critical or involved remote data. + +2000-08-08 Thomas Moestl + + * src/main.c: Changed FreeBSD signal latency to 250 ms. + + * src/main.c, acconfig.h: + A set of last-minute FreeBSD fixes. pdnsd does now NEED linuxthreads on + BSD. + +2000-08-07 Thomas Moestl + + * version: It's now called 1.0.7. + + * src/main.c, doc/pdnsd.conf.in, Makefile.am: Misc build&BSD fixes. + + * src/conff.c, src/conf-parse.y, version: + Version set to the hopefully last beta. Fixed the proxy_only option. + +2000-08-05 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_query.c, src/status.c, doc/pdnsd.conf.in, Makefile.am, TODO: + Added the proxy_only options. Some build fixups. + +2000-07-30 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/rc/Makefile.am, src/main.c, src/status.h, AUTHORS, INSTALL, Makefile.am, TODO, version: + Many small fixups for 1.0.7. + +2000-07-29 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/rc/README, src/status.c, src/servers.c, Makefile.am, TODO, acconfig.h: + Assorted fixes. + + * src/cache.c, src/hash.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/icmp.c, src/ipvers.h, src/servers.c, src/servers.h, src/status.c, src/status.h, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, AUTHORS, Makefile.am, THANKS: + Big heap of updates and fixes. Incorporated build changes from Sourav + K. Mandal and pcmcia SCHEME support by Stephan Boettcher. + +2000-07-22 Thomas Moestl + + * src/rc/Debian/pdnsd: + Applied a patch by Markus Mohr to his debian rc script, which I had + broken in some way. + +2000-07-21 Thomas Moestl + + * src/cache.c, src/main.c, src/status.c, src/status.h: + Worked on the new status socket (pdnsd-ctl) option. + + * src/Makefile.am, version: Upped version, fixed Makefile.am + + * src/cache.c, src/hash.c, src/hash.h, src/cache.h, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/dns_query.h, src/helpers.c, src/helpers.h, src/ipvers.h, src/main.c, src/status.c, src/status.h, AUTHORS, Makefile.am, THANKS, acconfig.h: + Updated AUTHORS and THANKS. Merged in patches by Sourav K. Mandal + and Lyonel Vincent. + +2000-07-20 Thomas Moestl + + * doc/pdnsd.conf: Added pdnsd.conf. Well... + + * src/dns_query.c, src/dns_query.h, doc/pdnsd.conf: + Added some ommited files. + + * src/test/Makefile.am, src/test/if_up.c, src/test/is_local_addr.c, src/test/random.c, src/test/tping.c, src/rc/SuSE/Makefile.am, src/rc/SuSE/pdnsd, src/rc/RedHat/Makefile.am, src/rc/RedHat/pdnsd, src/rc/Debian/pdnsd, src/rc/Makefile.am, src/rc/README, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_answer.h, src/error.c, src/error.h, src/helpers.c, src/helpers.h, src/icmp.c, src/icmp.h, src/ipvers.h, src/main.c, src/netdev.c, src/netdev.h, src/servers.c, src/servers.h, src/status.c, src/status.h, doc/pdnsd.conf.in, Makefile.am, acconfig.h, file-list.base.in, pdnsd.spec.in: + Checked in the pdnsd files at their new locations. + + * doc/pdnsd.conf, Makefile, a-conf.sh, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + Removed the moved files. Will add the new ones soon. + +2000-07-16 Thomas Moestl + + * TODO: New tasks in TODO. + +2000-07-15 Thomas Moestl + + * TODO: + Updated TODO: Autoconf support was contributed by Sourav K. Mandal + + * conff.h, dns_query.c, ipvers.h, cache.c: + Fixed some minor bugs and a showstopper in cache.c that caused + crashes in some situations. + +2000-07-12 Thomas Moestl + + * dns_query.c, error.c, error.h, main.c: + Made pdnsd ignore SIGPIPE, which seemed to be responsible for some + crashes. + Accept (grudgingly) SOA rr's where NS ones would be The Right Thing. + +2000-07-10 Thomas Moestl + + * AUTHORS, THANKS, conff.c: + Updated AUTHORS, THANKS, and the fprintfs for the status pipe in + conff.c + + * TODO, config.h.templ, dns.h, dns_answer.c, dns_query.c, dns_query.h, main.c, version: + Added UDP queries and gave the user the choice between TCP and UDP + queries (UDP is the default now). Made the TCP server optional. + Fixed a authoritative record handling bug. Added pidfile support. + +2000-07-07 Thomas Moestl + + * doc/pdnsd.conf: + Inserted run_as="nobody"; again, it is The Right Thing and people + should use it. + + * Makefile: + The pdnsd cache directory is now created as nobody, since the + default run_as in the example pdnsd.conf is also nobody. + + * doc/pdnsd.conf: + Commented the run_as option out (people may run into permission + problems). + + * version: Upped version to 1.0.5 + + * AUTHORS, THANKS, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, icmp.c, icmp.h, main.c, version: + Folded in the server_ip option code as contributed by Wolfgang Ocker + and extended it to IPv6. Fixed a bug in IPv4 ping in IPv6 mode. + +2000-07-06 Thomas Moestl + + * cache.c, dns_query.c: + Killed a bug which could cause crashes with more than 2 servers. + + * cache.c: Fixed a bug reported by Bert Frederiks that would break the + serve_aliases option when only one character was between official + name and alias in the /etc/hosts-style file. + +2000-07-04 Thomas Moestl + + * pdnsd-suse.spec.templ: The SuSE spec now uses the new makefile rule. + + * Makefile, THANKS, dns_query.c, helpers.c, version: + Added people to THANKS, fixed a bug that caused uppercase hosts/ + rr-section entries to be ignored in the cache, fixed the SuSE + makefile for pdnsd to run_as nobody, and other small fixups. + +2000-07-03 Thomas Moestl + + * dns_answer.c: + First change after release of 1.0.4: The questions received + are now properly written into the debug file when starting + with -g -d. + + * config.h.templ, dns_query.c: + Fixed a possible way to get around paranoid restrictions. + + * version: Set version to 1.0.4 + + * doc/pdnsd.conf: Added an entry for the paranoid option. + + * cache.c, config.h.templ, dns_answer.c, dns_query.c, ipvers.h: + Revisions and fixups. The complete code revision is now complete. + +2000-06-29 Thomas Moestl + + * dns_query.c: Overhaul. + + * dns_answer.c, dns_query.c, config.h.templ: + Code overhault continued. dns_answer.c is finished. + +2000-06-27 Thomas Moestl + + * conff.c, dns_answer.c, icmp.c, netdev.c, servers.c, status.c: + Continued code overhaul. Fixed several bugs, and simplified some + code. + + * conf.l.templ, conf.y, conff.c, ipvers.h, version: Fixups. + +2000-06-26 Thomas Moestl + + * hash.c: Revised; fixed a minor bug. + + * cache.c: Overhauled. + + * dns_query.c, error.c, helpers.c, helpers.h, ipvers.h, main.c, version, dns.c: + Manual code overhaul. Numerous small patches, greatly simplified + decompress_name(). + +2000-06-25 Thomas Moestl + + * config.h.templ: + Made the C random() RNG the default (using /dev/urandom, we suck up + too much randomness on high load). + + * error.c, error.h, icmp.c, icmp.h, main.c: + Small cleanups. Makes the testsuite compilation easier. + + * Makefile, config.h.templ: Preparing for release of 1.0.4. + +2000-06-24 Thomas Moestl + + * Makefile, THANKS, a-conf.sh, dns_answer.c, dns_query.c, error.c, version: + Fixed bugs with the paranoid option, connect() timeout handling, and + a incompatability in response handling that caused the glibc + resolver to misunderstand error messages pdnsd generated on unknown + query types. This bug, that was reported by James MacLean, could + for example cause ssh to hang some time. + + * Makefile, NEWS, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, dns.c, dns_query.c, dns_query.h, helpers.c, helpers.h, icmp.c, icmp.h, main.c: + Added the paranoid option, and modified the ping uptest so that it + works with strict_setuid. Also made strict_setuid=on the default. + 1.0.4 should be out soon. + +2000-06-23 Thomas Moestl + + * doc/pdnsd.conf: Added a run_as= line, which is sensible normally. + + * Makefile, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, dns_answer.h, helpers.c, helpers.h, main.c, servers.c, status.c, version: + Some fixups, added the run_as and strict_setuid security options. + + * THANKS: Updated. + + * AUTHORS, ipvers.h, main.c, netdev.c, version: + Fixed some definitions for glibc2.0-users. Repaced the return at the + end of main() with _exit(). Should not build and run OK on glibc 2.0 + boxen. + Fixed a typo in netdev.c + + * ipvers.h: + Fixed a typo in ipvers.h to fix compile problems on systems without + an IPv6-supporting C library, and possible IPv6 problems using the + status pipe. + + * error.c, version: + Fixed a bug that could cause signals to be delivered to the wrong + process. + +2000-06-22 Thomas Moestl + + * version: Set version to 1.0.1. + + * cache.c, dns_answer.c, error.c, error.h, main.c, pdnsd-suse.spec.templ: + Fixed misc issues reported by Jonathan Hudson and Joachim Dorner, one + of them a real showstopper in cache.c. + + * Makefile, NEWS, README, cache.h, config.h.templ, version: + Updated things for 1.0.0 and released it finally. + + * AUTHORS, THANKS: Updated THANKS and AUTHORS + + * NEWS, a-conf.sh, cache.c, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, dns_query.c, error.c, error.h, main.c, servers.c, status.c: + Fixed a-conf.sh and cleaned up signal handling as far as it can be + done ;-). Added the max_ttl option. + +2000-06-21 Thomas Moestl + + * dns_answer.c, error.c, error.h, main.c, servers.c, status.c: + More signal fixes. This is a real pain with LinuxThreads. + + * NEWS, cache.c, dns_answer.c, error.c, error.h, helpers.c, helpers.h, main.c, servers.c, status.c, version: + Fixups for signal handling. This is more than only a little tricky + using the linuxthreads library. This hopefully fixes the deadlocks + we had on signals. + +2000-06-13 Thomas Moestl + + * AUTHORS, THANKS: Updated credits. + + * a-conf.sh: + A primitive configure-like script intended as drop-in replacement + until autoconf support finally comes. + + * .cvsignore, Makefile, cache.c, dns_query.c, ipvers.h, servers.c, version: + Added a primitive configuration script as drop-in. Killed some bugs + and changed the recently added linkdown_kluge option following + suggestions from Daniel Smolik. + +2000-06-12 Thomas Moestl + + * dns_query.h, exec-flex.sh, Makefile, cache.c, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, dns_query.c: + Numerous cleanups and fixes. Implemented the linkdown_kluge option + as proposed by Daniel Smolik. Hope to get ready for 1.0.0 know. + +2000-06-10 Thomas Moestl + + * Makefile, NEWS, TODO, dns_answer.c: + Modified some stuff in dns_answer.c (if no nameserver for a knot in + the dns namespace is found now, its predecessors are tried now in + order to return accurate authority results). This will be paid with a + little more beta time, so the Makefile has developer switches again. + Corrected NEWS and TODO. + + * Makefile, config.h.templ, dns_answer.c, dns_query.c, dns_query.h, hash.h, helpers.c, version: + Removed some dead code, fixed some really minor bugs. Version is up + to 1.0.0p7, which is hopefully the last beta. + + * Makefile, config.h.templ: + Fixed things up for the 1.0.0 distribution version + +2000-06-06 Thomas Moestl + + * Makefile, config.h.templ, icmp.c, netdev.c: + Some minor comment fixes. + + * Makefile, TODO, main.c, version: + BSD fix in Makefile and help update. It is now called 1.0.0p6. TODO + was updated to reflect the project status. + + * dns_answer.c: + BSD & misc fixes. pdnsd runs now nicely on my FreeBSD 4.0 box. + + * dns_answer.c, ipvers.h: + Fixed IPv6 UDP dest address recovery. Also fixed a real stupid bug in + ipvers.h. + + * cache.h, dns_query.c, error.h: + Added DEBUG_MSG6 macros. Cleaned up requery handling. + + * dns.c, dns_answer.c, dns_query.c, dns_query.h: + Fixed another heap of bugs, introduced some sanity checks, no requery + on answers that have ra not set now. + +2000-06-05 Thomas Moestl + + * cache.c: + Fixed write_disk_cache. + + * cache.c, cache.h: + Fixes for rr handling. + +2000-06-04 Thomas Moestl + + * cache.c, dns_answer.c, dns_query.c: + Fixes again: some missing checks for rrset existence added. + + * cache.c, dns_answer.c, helpers.c, icmp.c: + Fixes for the new/modified code and its side effects on old code ;-) + + * ChangeLog.old, NEWS, cache.c, conf.l.templ, conf.y, conff.c, config.h.templ, dns.c, dns_answer.c, dns_query.c, error.c, hash.c, helpers.c, icmp.c, main.c, netdev.c, servers.c, status.c: + Folded the ChangeLog and NEWS of the 0.9.x tree back in and added NEWS for the + upcoming 1.0.0 release. Some compile fixes. Reorganized config.h.templ. Made + the inclusion of the rcsid strings into the executable optional. + + * cache.c, dns_answer.c: + Pile of fixes on recently added/modified code. + +2000-06-03 Thomas Moestl + + * .cvsignore, Makefile, lex.inc.h: + lex.inc.h should not be in CVS (it is automatically generated by + exec-flex.sh). It should also be deleted by 'make mclean'. + + * TODO, cache.c, dns_answer.c, dns_query.c, lex.inc.h: + rfc2181 conformance should be reached by now. Updated TODO. Bugfixing + remains. + + * doc/html/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html: + Removed the html documentation from CVS. It is maintained separately. + + * doc/pdnsd.conf: + Added CVS/RCS $Id$ tag. + + * cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version, AUTHORS, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c: + Added CVS/RCS $Id$ tags to most files, did some cleanups, introduced + the new rrset granularity caching. The new code is still much of beta, + use with care. + +2000-06-01 Thomas Moestl + + * Makefile, cache.c, hash.c, helpers.c, icmp.c, netdev.c: + Yet another set of BSD fixes (test programs do now work for me + under FreeBSD). Some other minor fixes. + + * Makefile, error.c, error.h, helpers.c, helpers.h: + Transplanted kill_pdnsd from error.c to helpers.c in order to get the + tests compiled without the thread library. + +2000-05-31 Thomas Moestl + + * pdnsd: + Ooops, executable got in. + + * Makefile, pdnsd: + Added test suite programs. + + * icmp.c, netdev.c: + All basic BSD patches have been folded in. pdnsd will now compile on + FreeBSD with (hopefully) all features. + + * dns_answer.c, Makefile: + Disabled udp targed address discovery for BSD builds (this sadly ist + OS specific at least for IPv4. Must be rewritten under BSD as it is + an RFC compatability issue under some circumstances) + + * cache.c, cache.h, conf.y, conff.c, conff.h, config.h.templ, dns_answer.c, helpers.c, icmp.c, ipvers.h, netdev.c, Makefile: + BSD include & misc build fixes. More to follow... + + * Makefile, cache.h: + BSD Fixes: Makefile should work with BSD make, sed command line, + sorted out naming clash in cache.h + + * .cvsignore: + Added ChangeLog to .cvsignore + + * THANKS, conff.c: + Fixed a bug reported by Jonathan Hudson and added him to THANKS + + * ChangeLog.old: + Added the pre-CVS ChangeLog. + + * .cvsignore, AUTHORS, COPYING, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, doc/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html, doc/pdnsd.conf, doc/txt/.cvsignore, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, lex.inc.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + Initial import of pdnsd-1.0.0p3 source tree into CVS. + + + * .cvsignore, AUTHORS, COPYING, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, doc/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html, doc/pdnsd.conf, doc/txt/.cvsignore, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, lex.inc.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + New file. + diff --git a/app/src/main/jni/pdnsd/ChangeLog.old b/app/src/main/jni/pdnsd/ChangeLog.old new file mode 100644 index 0000000..0b3553e --- /dev/null +++ b/app/src/main/jni/pdnsd/ChangeLog.old @@ -0,0 +1,161 @@ +2000-02-15 + Version 0.2. First working alpha with the extended feature set. + +2000-02-16 + Version 0.3 with many bugfixes, better standard conformity and + some new features. + +2000-02-17 + Did a lint on the code, implemented soft timeouts, again bugfixes, + drastically reduced cache memory requirements, implemented local + records. + Version 0.4 released. + +2000-02-19 + Recursive query finally implemented. Version 0.5 out. + +2000-02-20 + Various bugfixes. The server now always tries to get an AA answer + if possible; this implies recursion. This is to deal better with + caching servers that may return incomplete results on * queries. + This may be a little sub-optimal since it may not take the full + effect of caching, but it is the only real possibility of getting + complete records. + Redid the deps in the makefile for now. + Version is now 0.6. + +2000-02-21 + Another set of bugfixes. Version is 0.6.1. + +2000-02-22 + Another set of bugfixes. It should stabilize by now. Answer compression + is there finally. Version is therefore up again, 0.7 by now. + +2000-02-23 + Minor bugfixes, isdn interface uptest added. The new record types as of + rfc1700 are implemented, but as a compile-time option, since normally + there is no need to waste space for them. + Version is 0.7.1. + +2000-02-24 + Version 0.7.2 with all rrs up to KX (36). #define DNS_NEW_RRS in + config.h and (re)compile if you want to use them. Delete the cache + file before using a version with this option changed! + NSAP-PTR does NOT WORK (any incoming answer containing it may cause + a format error) because it is ill-designed (see TODO). Never mind, it + is officially deprecated anyway. + The secure DNS extension record types defined in RFC 2065 (KEY, SIG, + NXT) are cached only and therefore useless. + +2000-02-26 + Version 0.8 with parallel query (and probably new bugs) introduced. + +2000-02-27 + Version 0.8.1 with minor bugfixes on the new features. + +2000-02-27 + Version 0.8.5 with authority support and the usual bugifxes. Some issues + with standard conformity were also fixed (wow, two versions a day). + +2000-02-28 + Implemented caching of non-authoritative records. This allows better + usage of other caching name servers. This may return non-authoritative + records to any non-wildcard query. If a wildcard query arrives, we + always look for authoritative records, so we do if the non-authoritative + answer does not contain at least one answering record to the query. + This fixes the sub-optimal behaviour since 0.6. + Version is therefore up to 0.8.7 (0.8.6 was also released today + containing bugfixes) + +2000-02-29 + Better thread support, avoiding deadlocks in signal handling. The cost + is unfortunately one more thread. + New option server_port in config file. + Version is 0.8.8. + +2000-03-01 + Nailed a renitent memory leak bug to the wall (shame, what a trivial + mistake). Also improved handling of error conditions slightly. + Version is 0.8.9. + +2000-03-01 + Cache code cleanup. The development of the non-parallel query code + is discontinued, although it still works. If you want serial query, + just set PAR_QUERIES to 1. + Additional information finally implemented. + We are up to 0.9. + +2000-03-02 + Fixed a DoS possibility. + More rfc compatability fixups and a smarter resolver logic. + Version 0.9.1 + +2000-03-02 + Added the source section to the config file handling. This allows to + source your /etc/hosts file. Version 0.9.2. + +2000-03-02 + Avoiding double additional records now. Version 0.9.3. + +2000-03-09 + Fixed some REALLY STUPID bugs. Uuummph, thought it was finished. + However, the number of bugs remaining is always n+1... + Anyway, we are up to 0.9.4. Hope that there are no mean bugs left. + +2000-03-14 + Added another uptest, exec. See README for details. Also grained the + cache size finer (it's now specified in kB). Version 0.9.5 + +2000-04-07 + Fixed some really stupid bugs, what else? Version is 0.9.6 + To be more exact: fixed misc bugs, cleaned up hash.c and cache.c + +2000-04-20 + Again fixed some bugs, version is now 0.9.7. + +2000-04-29 + Fixed a build problem caused by some missing #defines in glibc2.0 and a + minor bug. + Version is up to 0.9.8 + +2000-04-30 + Fixed some unclean C code and did a general C lint. Thanks to Byrial + Jensen for pointing out some issues. Now using stricter compiler flags. + Also replaced the daemon() call in main.c for improved portability. + Pre-Released 0.9.9p1 + +2000-05-03 + Released 0.9.9p2 with the changes of 0.9.9p1, Documentation updates, + bugfixes, and the Red Hat rc scripts contributed by Torben Janssen. + Also, the meaning of the command line option -v has changed. + There is a new config file option "lean_query" that is on by + default. It is an optimization, so please look in the docs when + updating whether you want it switched on or not. + Removed the long-dead serial query code from the distribution tree. + Some resolvers seem to be broken somehow in a respect that it cares + about order in which the records appear. In particular, it wants + cname to appear before other records. Ok, so it be... + + +2000-05-04 + Save all names in lower case in the rrs. + Tidying up the source tree. + The long-awaited cache structure changes have been started. Please + delete you cache files before using this new release. + +2000-05-05 + Fixed several bugs in the old and in the new source tree. + Use time_t instead of long for internal time storage for compatibility. + + +2000-05-06 + Version 0.9.10. + This fixes a bug in uptest=if. Red Hat and configuration fixes + suggested by Soeren J. Peters were included. + +2000-05-08 + Version 0.9.11 + This fixes a locally exploitable security problem (pdnsd.cache was + world-writeable). This is actually a one-line fix; for a description of + possible dangers, please refer to the html documentation. + Thanks to Alan Swanson for reporting. diff --git a/app/src/main/jni/pdnsd/INSTALL b/app/src/main/jni/pdnsd/INSTALL new file mode 100644 index 0000000..0399189 --- /dev/null +++ b/app/src/main/jni/pdnsd/INSTALL @@ -0,0 +1,190 @@ +The installation instructions are in doc/html/doc.html and doc/txt/manual.txt. +The system requirements are listed in doc/html/index.html and doc/txt/intro.txt. +I recommend using the html version. +Following are generic installation instructions for autoconf programs. +I strongly recommend to read the Installation section in the docs! + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/app/src/main/jni/pdnsd/Makefile.am b/app/src/main/jni/pdnsd/Makefile.am new file mode 100644 index 0000000..146c32a --- /dev/null +++ b/app/src/main/jni/pdnsd/Makefile.am @@ -0,0 +1,40 @@ + +SUBDIRS = src doc contrib + +EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD + +# The sample configuration is handled in doc/Makefile.am +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(cachedir)" + test -f "$(DESTDIR)$(cachedir)/pdnsd.cache" || \ + touch "$(DESTDIR)$(cachedir)/pdnsd.cache" + if test `whoami` = "root"; then \ + chown $(def_id) "$(DESTDIR)$(cachedir)/pdnsd.cache"; \ + chown $(def_id) "$(DESTDIR)$(cachedir)"; \ + fi + chmod 0640 "$(DESTDIR)$(cachedir)/pdnsd.cache" + chmod 0750 "$(DESTDIR)$(cachedir)" + +dist-hook: $(PACKAGE).spec.in + sed -e '/^%{!?distro: %define distro /c\ + %if 0%{!?distro:1}\ + %if "%{_vendor}" == "redhat"\ + %define distro RedHat\ + %else\ + %if "%{_vendor}" == "suse"\ + %define distro SuSE\ + %else\ + %if "%{_vendor}" == "SuSE"\ + %define distro SuSE\ + %endif\ + %endif\ + %endif\ + %endif' \ + -e 's:[@]PACKAGE[@]:$(PACKAGE):g' \ + -e 's:[@]VERSION[@]:$(VERSION):g' \ + -e 's:[@]fullversion[@]:$(fullversion):g' \ + -e 's:[@]packagerelease[@]:$(packagerelease):g' \ + -e 's:[@]cachedir[@]:/var/cache/$(PACKAGE):g' \ + -e 's:[@]def_id[@]:$(PACKAGE):g' \ + $(PACKAGE).spec.in > $(distdir)/$(PACKAGE).spec + diff --git a/app/src/main/jni/pdnsd/Makefile.in b/app/src/main/jni/pdnsd/Makefile.in new file mode 100644 index 0000000..a865d54 --- /dev/null +++ b/app/src/main/jni/pdnsd/Makefile.in @@ -0,0 +1,734 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = . +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/PKGBUILD.in \ + $(srcdir)/config.h.in $(srcdir)/file-list.base.in \ + $(srcdir)/pdnsd.spec.in $(top_srcdir)/configure AUTHORS \ + COPYING ChangeLog INSTALL NEWS THANKS TODO acconfig.h compile \ + depcomp install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = pdnsd.spec file-list.base PKGBUILD +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir dist dist-all distcheck +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d "$(distdir)" \ + || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr "$(distdir)"; }; } +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = src doc contrib +EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) $(top_srcdir)/acconfig.h + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +pdnsd.spec: $(top_builddir)/config.status $(srcdir)/pdnsd.spec.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +file-list.base: $(top_builddir)/config.status $(srcdir)/file-list.base.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +PKGBUILD: $(top_builddir)/config.status $(srcdir)/PKGBUILD.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @$(am__cd) '$(distuninstallcheck_dir)' \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ + ctags-recursive install-am install-data-am install-strip \ + tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am am--refresh check check-am clean clean-generic \ + ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ + dist-hook dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-hook install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ + tags-recursive uninstall uninstall-am + + +# The sample configuration is handled in doc/Makefile.am +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(cachedir)" + test -f "$(DESTDIR)$(cachedir)/pdnsd.cache" || \ + touch "$(DESTDIR)$(cachedir)/pdnsd.cache" + if test `whoami` = "root"; then \ + chown $(def_id) "$(DESTDIR)$(cachedir)/pdnsd.cache"; \ + chown $(def_id) "$(DESTDIR)$(cachedir)"; \ + fi + chmod 0640 "$(DESTDIR)$(cachedir)/pdnsd.cache" + chmod 0750 "$(DESTDIR)$(cachedir)" + +dist-hook: $(PACKAGE).spec.in + sed -e '/^%{!?distro: %define distro /c\ + %if 0%{!?distro:1}\ + %if "%{_vendor}" == "redhat"\ + %define distro RedHat\ + %else\ + %if "%{_vendor}" == "suse"\ + %define distro SuSE\ + %else\ + %if "%{_vendor}" == "SuSE"\ + %define distro SuSE\ + %endif\ + %endif\ + %endif\ + %endif' \ + -e 's:[@]PACKAGE[@]:$(PACKAGE):g' \ + -e 's:[@]VERSION[@]:$(VERSION):g' \ + -e 's:[@]fullversion[@]:$(fullversion):g' \ + -e 's:[@]packagerelease[@]:$(packagerelease):g' \ + -e 's:[@]cachedir[@]:/var/cache/$(PACKAGE):g' \ + -e 's:[@]def_id[@]:$(PACKAGE):g' \ + $(PACKAGE).spec.in > $(distdir)/$(PACKAGE).spec + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/NEWS b/app/src/main/jni/pdnsd/NEWS new file mode 100644 index 0000000..9a83bba --- /dev/null +++ b/app/src/main/jni/pdnsd/NEWS @@ -0,0 +1,324 @@ +Version 1.2.9a fixes a bug in the 1.2.9 release that causes a build failure when +pdnsd is configured with --enable-strict-rfc2181. Unless you use this option to +compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. + +Version 1.2.9 has support for many additional RR types, in particular those +needed for DNSSEC (though no support for the DNSSEC protocol itself yet in +pdnsd). Caching data structures are now more efficient when they only store the +most commonly used RR types. Fine-grained configurability over which RR-types +are cache-able. Pdnsd now has support for EDNS (Extension mechanisms for DNS), +although its usefulness is currently limited to enabling UDP messages larger +than 512 bytes. Defining local TXT records in the configuration file is now +supported. A new configuration option provides a fix in case the query uptest +fails due to remote servers ignoring empty queries. Several bugs have been fixed +including a UDP socket descriptor leak that affected the FreeBSD platform, and +an IPv6 port binding bug. + +Version 1.2.8 implements support for automatic discovery of root servers. +There are also some improvements in the resolver and a new default setting for +the neg_rrs_pol configuration option. + +Version 1.2.7-par fixes some security problems. It contains a fix for a +"dangling pointer" bug that could cause pdnsd to crash when it received a long +reply. It also addresses some of the issues raised in the CERT vulnerability +note VU#800113 by making the default of query_port_start equal to 1024, thereby +ensuring that source ports are randomly selected by the pdnsd resolver in the +range 1024-65535. This release also fixes problems with compiling pdnsd for the +ARM architecture and for the Darwin platform (Max OS X). There are a number of +(minor) new features. pdnsd now supports "include" files, essentially +configuration files that only contain definitions for local records. It is now +possible to define interactively, using pdnsd-ctl, any local record that can be +defined in a configuration file. + +Version 1.2.6-par has an upgraded license: GPL version 3. +A bug has been fixed which which caused pdnsd to handle NXDOMAIN replies +inefficiently when configured with neg_domain_pol=on. Also the code for the +ping test has been fixed, which was broken for 64-bit systems. A new option +randomize_servers can be used to give each server in a section of the +configuration file an equal chance of being queried. The new options reject, +reject_policy and reject_recursively make it possible to check for the presence +of certain IP addresses in the replies of name servers and to correct some types +of unwanted replies or to censor these IP addresses. +The pdnsd-ctl 'add a' and 'add aaaa' commands now allow multiple IP addresses to +be specified for the same name. There are some further improvements to pdnsd's +recursive resolver. + +Version 1.2.5-par introduces a new query method: udp_tcp. With this method a UDP +query is tried first and, if the UDP answer is truncated, the query is repeated +using TCP, which is the behaviour that seems to be recommended by the DNS +standards. There is a new configuration option use_nss, which can be turned off +to prevent lengthy timeouts and stalls in certain situations. A bug has been +fixed which could cause pdnsd to crash if debug output was generated before the +debug output stream was properly initialized. + +In version 1.2.4-par a memory leak and a minor buffer-overflow problem have been +fixed. There is now a fix for some situations that would previously cause pdnsd +to exit prematurely (such as ACPI S3 sleep or trying to attach strace to pdnsd). +Time intervals specified in the configuration file can now be expressed in +minutes, hours, days and weeks as well as seconds. Support for Apple Mac OS X +v10.4 Tiger has been improved. The "pdnsd-ctl status" command now also provides +some information about the status of the running threads. There are some further +improvements in the debugging information provided by pdnsd. +TCP-query support is now compiled in by default (but can still be disabled using +the configure option --disable-tcp-queries). + +In version 1.2.3-par the "pdnsd-ctl empty-cache" command can be provided with an +include/exclude list, allowing the user to specify a selection of names to be +removed, instead of emptying the cache completely. +Additional improvements: pdnsd should now remain responsive while executing the +"pdnsd-ctl empty-cache" command. With the query_method=tcp_udp option pdnsd will +now also try a UDP query after a TCP connection times out, which should allow +pdnsd to resolve the same names with query_method=tcp_udp as with +query_method=udp_only, although perhaps with an occasional delay. +"pdnsd-ctl config" or "pdnsd-ctl server" commands should now run without delays, +even if pdnsd is performing ping or query uptests at the time. A problem with +resolving certain names using root servers has been fixed. + +Version 1.2.2-par has a number of important portability improvements. A bug has +been fixed that prevented pdnsd from compiling successfully on some 64 bit +architectures. The code for determining endianness (most significant or least +significant byte first) should now be more portable. This release has +(experimental) support for the Darwin (Apple Mac OS X) platform. On Linux +systems, the configure script will now try to detect automatically whether the +system implements the Native POSIX Thread Library, but the method used may not +necessarily be foolproof. In addition, the debug features have been improved and +should make it easier to find out why pdnsd considers some queries or replies +malformed. + +Version 1.2.1-par has improved support for non-Linux platforms. This release has +(experimental) support for the Cygwin platform, and should also fix some +compilation glitches that have been reported by FreeBSD users. + +Version 1.2-par is a new and improved version of pdnsd! Most of the changes +effect the internal workings of pdnsd, but there are also a number of +interesting new features (well, I think they are interesting). Among the bugs +fixed are two rather nasty ones which involve the handling of NXT and NAPTR +records and which can cause pdnsd to crash or abort. The new features include a +new server availability test which can be specified with uptest=query, support +for reading the DNS configuration from resolv.conf files, a new option for +optimizing the use of root servers, a new option that makes defining local +records for reverse resolving easier, support for defining wildcard records, a +new pdnsd-ctl command for reloading the config file without restarting pdnsd, +and a new pdnsd-ctl command for dumping information about the names stored in +the cache. +The documentation has also been updated: there is now a pdnsd.conf man page. For +a more complete list of the changes I'll have to refer you to README.par and the +ChangeLog. + +Version 1.1.11a-par contains a fix for FreeBSD users that bypasses a problem +with the macro ENONET, which can cause a compilation failure when it is +undefined. Linux users will notice no difference between 1.1.11a-par and +1.1.11-par. + +Version 1.1.11-par has a rather large number of small changes, which are rather +difficult to summarize. Among the bugs fixed are a race condition in the cache +lookup code, a flaw in the code that caused a busy spin when a remote server +answered with "Not Implemented", and problems with the -4 and -6 command-line +options. Among the improvements are an alternative sorting algorithm which +should allow pdnsd to start up faster when reading a large cache file from disk, +automatic mapping of IPv4 to IPv6 addresses when running in IPv6 mode, somewhat +more efficient memory use, better compression of the replies and changes in the +parallel querying algorithm that should improve the chances of catching a reply +from a remote server. For a more complete list of the changes I'll have to +refer you to README.par and the ChangeLog. + +Version 1.1.10-par has a new parser for configuration files, completely +rewritten from scratch in C. The main advantages are: (f)lex and yacc/bison are +no longer needed to build pdnsd, more informative error messages instead of +merely "parse error", and string literals no longer need to be enclosed in +quotes in most cases. Furthermore, a bug has been fixed that caused incorrect +IPV6-type PTR records to be generated when sourcing /etc/hosts like files. +There have been other small changes, more details can be found in the ChangeLog. + +Version 1.1.9-par adds some missing pieces to the documentation (the pdnsd +manual and the man page for pdnsd-ctl). The changes to the code consist mostly +of optimizations, removal of some size limits due to fixed-size buffers, and +some cleaning up. I've also tried to make the error responses of pdnsd-ctl more +helpful. More details can be found in the ChangeLog. + +Version 1.1.8b1-par8 introduces a "delegation-only" feature that may be useful +for blocking Verisign's Sitefinder. The parser for the configuration file now +tolerates domain names missing a dot at the end. I have provided alternative +implementations for some GNU extensions that I used in an effort to make the +code more portable. In particular, the code should build on FreeBSD again. More +details can be found in the README.par file. + +Version 1.1.8b1-par7 fixing a number of bugs. I have also reworked some of the +code for adding and removing entries in the cache in an effort to improve +efficiency and stability. More details can be found in the ChangeLog. + +Version 1.1.8b1-par6 introduces some further code cleanup. In addition the +documentation has been revised. + +Version 1.1.8b1-par5 fixes a troublesome allocation size error that has been +discovered in Thomas Moestl's code. In practice this bug only wastes memory but +it could also potentially lead to memory corruption. Upgrading is +recommended. More details can be found in the ChangeLog. + +Version 1.1.8b1-par4 has been released. Due to incompatibilities between +various implementations of the pthread library on Linux systems, problems can +occur with signal handling in pdnsd. The usual symptom is failure by pdnsd to +save the cache to disk, and /var/cache/pdnsd/pdnsd.cache remaining empty. If you +experience this kind of trouble, try reconfiguring with different values for the +new --with-thread-lib option. The allowable values are described in the +documentation. + +pdnsd is no longer maintained by Thomas Moestl: I have not had time to maintain +pdnsd for quite a while now, and have been very slow to respond to issues, or +did not respond at all. It is time that I officially announce that pdnsd is no +longer actively maintained; I apologize to all those who reported bugs or asked +questions without receiving any reply. However, Paul A. Rombouts has published a +patch set against the last released version at +http://www.phys.uu.nl/~rombouts/pdnsd.html, which cleans up a lot of code fixes +many bugs. + +Version 1.1.7a fixes a reversed assertion that would cause pdnsd to terminate +if used with the ping uptest. No other changes were made. + +Version 1.1.7 fixes some problems that might be remotely exploitable to +gain access as the user pdnsd runs as (an unprivileged user by default). To do +this, an attacker needs to control a name server that is queried by pdnsd, and +send a malicious reply to such a query. Upgrading is strongly recommended! +There are also minor bug fixes and stability improvements. + +Version 1.1.6 adds the query_port_start and query_port_end options (contributed +by Andreas Steinmetz), that allow confining the ports pdnsd uses for outgoing +queries to a certain range. It also fixes numerous bugs, one of which could +cause pdnsd to hang; update is therefore recommended. + +Version 1.1.5 contains a fix for a security bug that would allow local users +that are allowed to use pdnsd-ctl on a running pdnsd server to execute +arbitrary code as the user that pdnsd runs as (or on Linux, when strict_setuid +is not enabled, as the user that started pdnsd). The danger of this is usually +quite limited; the status socket is not enabled by default, it's default +permissions do only allow the user pdnsd runs as to use the socket, +strict_setuid is enabled by default and pdnsd runs as an unprivileged user. +There is also a new configure option, --enable-underscores, that will make +pdnsd allow underscores in domain names. Furthermore, the SRV record handling +has been fixed to allow underscores in any case (this was not allowed +previously, but is required by the RFC). SOA records are not put in the +answer section any more if no answers are found (this violates the RFC's). +It may be put in the authority section in a later version. +There are also various bugfixes in this release. +Upgrade is recommended. + +Version 1.1.4 fixes various smaller bugs, and should also improve the cache +write performance especially for larger caches. There are also two new +features: servers can now be given a label (using the label server option) +which can be used to identify them for the pdnsd-ctl server command +(contributed by Andrew M. Bishop), and local records can be marked to make +the domain record authoritative in pdnsd's cache (which means that pdnsd will +assume that records that are not present in the cache for that domain are +non-existent); this is on by default now, and can be controlled using the new +authrec server option). + +Version 1.1.3 added contrib/ and had a lot of robustness fixes. +This release addresses a security hole that affects only Linux systems. Due to +a bug in glibc, pdnsd could crash during a port scan. This release contains +a workaround for this, as well as a fix for a deadlock under heavy load +conditions. It also fixes a possible problem that could be triggered by +malicious servers, and contains numerous bug fixes. +A script, contributed by Marko Stolle, makes pdnsd useful in a DHCP setup. +pdnsd also preservers the case of names in the cache, and should work much +better on alpha machines (thanks for the contributions by Bjoern Fischer +and P.J. Bostley that made this possible). New types were dded for rr +sections and pdnsd-ctl. +Upgrade is recommended. + +Version 1.1.2 has a fix for a bug that could cause SERVFAIL to be +returned when NXDOMAIN would be appropriate. The bug surfaced only when +pdnsd queried name servers with a behaviour different from BIND's in the +NXDOMAIN case, e.g. pdnsd querying another pdnsd or e.g. djbdns. + +Version 1.1.1 fixes a possible race condition in status socket creation. +This race might be used by a local attacker to change the access +permissions of a certain file in /tmp. The risk of this is probably +negligible. The default setup uses a non-privileged user, default mode +0600, and the status socket is disabled normally, so this should be +relatively safe. I don't see any possibility to exploit this, it is +more of a paranoia fix. +There are also some other minor fixes and documentation improvements. +Upgrade is recommended. + +Version 1.1.0 introduces negative cacheing, pdnsd-ctl enhancements and +a much improved FreeBSD support. The cache file format has changed from +prior releases. Some configuration defaults have changed, too. + +Version 1.0.15 is mostly a bugfix release. It also has a new option: +randomize_recs in the global section. + +Version 1.0.14 has a fix in icmp.c that will make it build properly +on FreeBSD and older Linux systems. + +Version 1.0.13 has some code cleanup, a fix for the Debian rc install, +and a security fix (contributed by Olaf Kirch): when changing +user and group id, pdnsd did not drop supplementary group IDs that +the original user was member of. + +Version 1.0.12 is a bugfix release and contains some security +enhancements. There are also inclusion/exclusion lists for servers +(new options include=, exclude=, policy= in the server +section). + +Version 1.0.11 fixes two bugs that might be used for denial-of-service +attacks, upgrading is recommended. + +Versions 1.0.9 and 1.0.10 are bugfix releases. + +Version 1.0.8 introduces special linux ppp device support contributed +by Ron Yorston, and has some bugfixes. + +Version 1.0.7 introduces autoconf support, many new config file options and +the new pdnsd-ctl run-time configuration program. + +Version 1.0.6 has another set of bugfixes, in addition to higher compile- +time configurability and UDP query support. It also contains Debian rc +scripts contributed by Markus Mohr. + +Version 1.0.5 has some bugfixes and the new "server_ip" option +contributed by Wolfgang Ocker. + +Version 1.0.4 introduces the new options run_as, strict_setuid and +paranoid. These new options are optional security enhancements. + +Versions 1.0.1, 1.0.2 and 1.0.3 are bugfix releases. + +Version 1.0.0 has a lot of changes compared to the 0.9.x tree, but much of +them "under the hood": +- IPv6 support (experimental; compile- and run-time configurable) +- FreeBSD (and such hopefully *BSD) support +- better rfc2181 compatability +- new options: + - serve_aliases in source section + - linkdown_kluge in global section + - max_ttl in global section +- cache-code reorganization, only one unified hash (of variable depth) +- Optimizations & cleanups +- Automatic deps (only interesting for developers ;-) + +Version 0.9.11 fixes a locally exploitable security hole (the cache file was +world writeable by default). Please see ChangeLog.old for details. + +Version 0.9.10 fixes some bugs and improves build on Red Hat. + +Version 0.9.9 contains the rc scripts for Red Hat Linux contributed by Torben +Janssen, in addition to code cleanups and bugfixes. +The meaning of the option -v has changed in this release. +There is also a new config file option "lean_query" that is on by default. It +is an optimization, so please look in the docs when updating whether you want +it switched on or not. + +When compiling versions after 0.9.8, you will probably get more +compiler warningsthan before. This is because the C compiler settings +have been made stricter. + +Version 0.9.8 fixes a minor bug some build problems with glibc2.0 systems. + +The versions 0.9.6 and 0.9.7 are bugfix releases. + +Version 0.9.5 introduces uptest=exec, and a modified config file syntax (cache +sizes are now specified in kB). + +Version 0.9.4 was the first to be released to the public. For information on +changes, see ChangeLog. + diff --git a/app/src/main/jni/pdnsd/PKGBUILD.in b/app/src/main/jni/pdnsd/PKGBUILD.in new file mode 100644 index 0000000..3d61765 --- /dev/null +++ b/app/src/main/jni/pdnsd/PKGBUILD.in @@ -0,0 +1,24 @@ +# Package build script for Arch Linux, +# contributed by Alexander Drozdov. + +pkgname=@PACKAGE@ +pkgver=@VERSION@ +pkgrel=@packagerelease@ +pkgdesc="pdnsd is a proxy DNS server with permanent caching (the cache contents are written to hard disk on exit) that is designed to cope with unreachable or down DNS servers." +url="http://members.home.nl/p.a.rombouts/pdnsd.html" +license="GPLv3" +depends=() +makedepends=(glibc) +conflicts=() +replaces=() +backup=() +install= +source=(http://members.home.nl/p.a.rombouts/pdnsd/releases/$pkgname-$pkgver-$pkgrel.tar.gz) +md5sums=() + +build() { + cd $startdir/src/$pkgname-$pkgver + ./configure --prefix=/usr --enable-ipv6 --sysconfdir=/etc --with-distribution=ArchLinux + make || return 1 + make DESTDIR=$startdir/pkg install +} diff --git a/app/src/main/jni/pdnsd/README b/app/src/main/jni/pdnsd/README new file mode 100644 index 0000000..7042e93 --- /dev/null +++ b/app/src/main/jni/pdnsd/README @@ -0,0 +1,22 @@ +You can find the documentation for pdnsd in the doc/ directory. The html +documentation (which I recommend) is in the doc/html/ subdirectory. +The pure text documentation (which is generated automatically from the +html documentation) is in doc/txt/. +The following documents are available: + +index.html / intro.txt Overview, system requirements +doc.html / manual.txt Building, installation and usage instructions +faq.html / faq.txt The FAQ + +Share and enjoy! + Thomas + + +For news about recent changes in pdnsd the following files may be of +interest to you: + + README.par + ChangeLog + NEWS + +Last revised: 08 July 2007 by Paul Rombouts diff --git a/app/src/main/jni/pdnsd/README.par b/app/src/main/jni/pdnsd/README.par new file mode 100644 index 0000000..ea181bc --- /dev/null +++ b/app/src/main/jni/pdnsd/README.par @@ -0,0 +1,216 @@ + pdnsd version 1.2.9a by Paul Rombouts + ===================================== + +This file describes the version of pdnsd that I maintain personally and am +making available so other people can enjoy the latest features and fixes. Thomas +Moestl no longer maintains pdnsd himself, so I am effectively the new +maintainer. This README describes the new features in version 1.2. This version +has a rather large number of internal changes and also some new features, which +I am rather pleased with, even if I say so myself. I think the changes are +significant enough to warrant increasing the minor version number from 1.1 to +1.2. The differences between my previous "official" release 1.1.11 and Thomas' +last release 1.1.7a are described in my previous README, which I have renamed +REAME.par.old. In this README I restrict myself to describing changes between +1.1.11 and 1.2. +The main difference between versions 1.2 and 1.2.1, aside from some minor +changes, is that 1.2.1 has (experimental) support for the Cygwin platform. +Version 1.2.2 has further improvements in portability and should in +particular now also compile on the Darwin (Apple Mac OS X) platform. +Version 1.2.4 has some important fixes for a memory leak, a minor buffer- +overflow problem and some situations which could cause pdnsd to exit +prematurely. Note that TCP-query support is now compiled in by default, but can +still be disabled using a configure option. +The main new feature of version 1.2.5 is the new query method "udp_tcp". +Version 1.2.6 has an updated license: GPL version 3. The main new feature of +version 1.2.6 is the "reject" option, which makes it possible to censor or +correct for unwanted IP addresses in replies. +Version 1.2.7 contains an important fix for a "dangling pointer" bug and +attempts to make pdnsd less vulnerable to the issues raised in CERT +vulnerability note VU#800113. It also contains some improvements for defining +local records interactively using the pdnsd-ctl utility. +The main new feature of version 1.2.8 is automatic discovery of root servers, +as well as some minor improvements in the resolver. +Version 1.2.9 among other things supports many addtional RR types, uses data +structures that should be more slightly more memory efficient and has support +for EDNS, which allows DNS UDP messages to be larger than 512 bytes. +Version 1.2.9a is a simple bugfix release that fixes a problem with compiling +1.2.9 after configuring with --enable-strict-rfc2181. Unless you use this option +to compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. + +For instructions how to compile and install pdnsd see doc/html/doc.html or +doc/txt/manual.txt. Note that I am no longer distributing a patch w.r.t. Thomas' +version because the (compressed) patch file is barely smaller than the +(compressed) tar archive. + +Here follows a list of some of changes in version 1.2 from a user's perspective. +For a more technical description of some of the changes in the code see the ChangeLog. +For a short history about recent releases have a look at NEWS or doc/html/index.html. + +- First of all, two potentially rather nasty bugs have been fixed in the code + for the handling of NXT and NAPTR records. A response from a remote server + containing NXT records (even well-formed ones) will very likely cause pdnsd to + crash. The code for handling NAPTR records contained incorrect ASSERT + statements, which could cause pdnsd to abort in a controlled fashion, but + completely unnecessarily. + +- Sampo Lehtinen has remarked that pdnsd sometimes failed to resolve classless + reversed-delegated IP addresses, and that this has something to do with the + fact that pdnsd didn't accept '/' characters in domain names. After reading + some of the relevant RFCs I decided to remove all restrictions on the types + of characters that pdnsd accepts in domain names. Of course for most + applications, there are many characters which don't make sense in domain + names, but I feel that it is the responsibility of the client application to + reject these, not the proxy server. + +- At the suggestion of Dan Tihelka, I have expanded to the server_ip= option to + allow the name of an interface to be specified instead of an IP address. + Presently this has been tested on Linux only. Can someone running pdnsd on + *BSD tell me if the code for getting the address of an interface is different + for Linux and BSD-type systems? + +- At the suggestion of Juliusz Chroboczek I've added an new server availability + test which can be specified with uptest=query. This can be useful as an + alternative to "uptest=ping" in case the remote server does not respond to + ICMP_ECHO requests at all, which unfortunately is quite common these days. + "uptest=query" causes pdnsd to send an empty query to remote name servers. Any + well-formed response (apart from SERVFAIL) within the timeout period will be + interpreted as a sign that the server is "up". + +- Instead of specifying the IP addresses of the name servers that pdnsd should + query in a server section of the config file, you may also specify a + resolv.conf-style file. Preferably this should not be /etc/resolv.conf. If the + contents of the resolv.conf type file changes while pdnsd is running, you can + make pdnsd aware of the changes with the "pdnsd-ctl config" command, see + below. Example: + + server { + label=myisp; + file=/etc/ppp/resolv.conf; + timeout=10; + } + +- There is a new option for "server" sections in the config file: + root_server=on/off. + In case a server section contains only addresses of root servers, which + usually only give the name servers of top level domains in their reply, + setting root_server=on will enable certain optimizations. This involves using + cached information to reduce queries to the root servers, thus speeding up the + resolving of new names. + +- New option for "rr" sections in the config file: reverse=on/off. + If you want a locally defined name to resolve to a numeric address and vice + versa, you can now achieve this by setting reverse=on before defining the A + record, making it unnecessary to define a separate PTR record for the reverse + resolving. + Example: + + rr { + name = localhost; + reverse = on; + a = 127.0.0.1; + } + + has the same effect as: + + rr { + name = localhost; + a = 127.0.0.1; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + } + +- In rr sections it is now possible to specify a wildcard name, i.e. a name + starting with the label *. The * in a wildcard can match one or more labels in + a queried name, but only whole labels. For example, *.mydomain will match + a.mydomain or www.a.mydomain, but not mydomain. Before you can specify an rr + section with name=*.mydomain you must define some records for mydomain, + typically NS and/or SOA records. + Example: + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } + +- There is a slight backwards compatibility problem which involves the name= and + owner= options in rr sections. The new version does not allow you to place + owner= before name=. On the other hand, you may now freely mix the owner + option with the a,ptr,cname,mx and soa options and define as many records of + this type as you like (including zero). + +- pdnsd-ctl has three new commands: + + config: Reloads pdnsd's configuration file. This is more efficient than + restarting pdnsd, and should not cause only noticeable interruption in DNS + service. However, some types of configuration changes cannot be put into + effect this way, and you will be prompted to restart pdnsd instead. + + empty-cache: Empties the cache completely, freeing all existing entries. + In version 1.2.3 you can specify a selection of entries to delete by providing + a list of include/exclude patterns. + + dump: Prints information about all the names stored in the cached. This is + mainly useful for diagnostic purposes. + +- There is now a pdnsd.conf(5) man page, describing pdnsd's configuration file. + The man page has been generated from the html documentation using a customized + Perl script. + +- New in version 1.2.4: Time intervals in the configuration files can now be + expressed in seconds, minutes, hours, days and weeks, using the suffixes + s,m,h,d,and w. + Example: 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. + +- Version 1.2.5 introduces a new configuration option, contributed by Jan-Marek + Glogowski, called "use_nss" which can be turned off to prevent nasty delays in + certain situations. + Besides the query methods "udp_only", "tcp_only" and "tcp_udp" you can now + also specify "udp_tcp", which more closely adheres to the behaviour + recommended by DNS standards. + +- Version 1.2.6 introduces the "randomize_servers" and "reject" options. By + setting "randomize_servers" on you can give each server in a server section an + equal chance of being queried, which is useful when resolving from root + servers, for instance. The "reject" option can be used to censor certain IP + addresses or correct for unwanted replies from servers you don't completely + trust. + +- Version 1.2.7 contains support for "include" files which can be referenced + from configuration files or read interactively using pdnsd-ctl. These files + can be used to add local definitions to the cache without reconfiguring pdnsd. + The new "pdnsd-ctl eval" command can be used to interactively define local + records that could previously only be defined in configuration files but not + with the "pdnsd-ctl add" command. + +- Version 1.2.8 contains support for automatic discovery of root servers. + Instead of supplying a complete list of IP addresses of root servers in a + server section of the configuration file, you need only enter one or two + addresses of name servers which know the names and addresses of the root + servers and set "root_server=discover". + The "neg_rrs_pol" option has a new default setting, which should allow + sensible negative caching of RRs in most situations, even if "proxy_only=on". + +- Version 1.2.9 contains support for EDNS (Extension mechanisms for DNS), which + allows UDP messages to be larger than 512 bytes. Whether pdnsd uses EDNS in + outgoing queries is determined by the configuration option "edns_query". If + pdnsd receives a query using EDNS, it will reply using EDNS regardless of the + configuration settings. + Local TXT records can now be defined in the configuration file. + If the query uptest fails due to remote servers ignoring empty queries, this + can now be remedied using the new "query_test_name" config option. + +The new features are described in greater detail in the manual doc/html/doc.html +or doc/txt/manual.txt. + +Enjoy! + +If you have any questions about my version of pdnsd, you can send these +to . Questions about the original (unmaintained) pdnsd +version should be sent to or . diff --git a/app/src/main/jni/pdnsd/README.par.old b/app/src/main/jni/pdnsd/README.par.old new file mode 100644 index 0000000..4bf0eda --- /dev/null +++ b/app/src/main/jni/pdnsd/README.par.old @@ -0,0 +1,249 @@ + pdnsd maintenance version 1.1.11-par by Paul Rombouts + ======================================================= + +This file describes the version of pdnsd that I maintain personally and am +making available so other people can enjoy the latest features and fixes. +Thomas Moestl no longer maintains pdnsd himself, so I am effectively the new +maintainer. The current version is 1.1.11-par, which has a rather large number +of small changes. Among the bugs fixed are a race condition in the cache lookup +code, a flaw in the code that caused a busy spin when a remote server answered +with "Not Implemented", and problems with the -4 and -6 command-line options. +Among the improvements are an alternative sorting algorithm which should allow +pdnsd to start up faster when reading a large cache file from disk, automatic +mapping of IPv4 to IPv6 addresses when running in IPv6 mode, somewhat more +efficient memory use, and better compression of the replies. Some of the new +features are described in the second half of this file (look for "new in version +1.1.11"). For the rest of the changes I will have to refer you to the ChangeLog. +For a short history about recent releases have a look at doc/html/index.html. + +Since version 1.1.9 I've added some missing pieces to the documentation (the +manual doc/html/doc.html,doc/txt/manual.txt and the man page doc/pdnsd-ctl.8). +Version 1.1.11 finally has a man page doc/pdnsd.8, thanks to a contribution by +Mahesh T. Pai. + +The first part of this file describes how to patch, compile, install and run +pdnsd. The second part describes some of the changes I've made to Thomas +Moestl's code. + +Unless you're using the pre-patched source archive pdnsd-1.1.11-par.tar.gz you +must first apply my patch file pdnsd-1.1.11-par.diff.gz before compiling and +installing pdnsd according to Thomas Moestl's instructions described in the the +documentation. Use a freshly untarred copy of Thomas Moestl's original version +1.1.7a source, cd into the source directory pdnsd-1.1.7a and apply the command: + +gzip -cd /pdnsd-1.1.11-par.diff.gz | patch -p2 -N -Z + +Note: I have used GNU extensions so there may be some portability issues. I have +supplied alternatives for some of the less portable functions. There should be no +problem with most Linux distributions. + +That's it! You should now be able to compile, install and run pdnsd. See the +documentation in doc/html/doc.html or doc/txt/manual.txt for more detailed +instructions. + +Some people may want change the compiler optimization flag. I use the -O2 flag, +but it might be safer to use a lower level of optimization or no optimization at +all. In that case prefix the configure command with the desired compiler flags +like this (assuming you're using a bash shell): + + CFLAGS="-O1 -g -Wall" ./configure ... + +I have added a new configuration option "--with-thread-lib=", which you +should use if you experience problems with signal handling under Linux. The +usual symptom is failure by pdnsd to save the cache to disk, and +/var/cache/pdnsd/pdnsd.cache remaining empty. If you experience this kind of +trouble, try reconfiguring with different values for the --with-thread-lib +option. The allowable values are "linuxthreads" (the default), "linuxthreads2" +(or "lt2" for short), and "nptl". I recommend that you first configure and +compile without the --with-thread-lib option, then if you experience trouble try +again with --with-thread-lib=lt2 and recompile. +If your Linux system has an implementation of the Native POSIX Thread Library, +which is the case with Red Hat 9 for instance, you should use +--with-thread-lib=nptl . +Ideally, I would like to write a configure script which automatically detects +which kind of thread library is being used on a Linux system, but I don't have a +clue yet how to do this. If you can help me with this please write to me at the +email address listed at the end of this file. + +The rest of this file describes some of the modifications I've made, but you +don't have to read it if you simply want to run pdnsd as you're used to. + + +- The main new feature I've added enables you to change the server addresses + that pdnsd uses at run-time using pdnsd-ctl. I've done this because the ISPs I + use do not specify fixed DNS server addresses, but expect their clients to use + dynamic DNS configuration (DHCP in the case of the cable connection, RFC1877 + in case of isdn). I've extended the options that can be given with the + "server" command to pdnsd-ctl, to allow IP addresses to be specified as an + additional argument after "up|down|retest". This allows me to put something + like this in my ifup-local script: + + pdnsd-ctl server isp-label up "$DNS1 $DNS2" + + For more details how to use pdnsd-ctl read the updated documentation in + the doc/html directory. There is also a manpage for pdnsd-ctl. + This was quite tricky to implement because there might be pending queries + while the addresses are being changed. It certainly was an interesting + exercise in writing multi-threaded code for me. + + +- I've implemented a feature which allowed me to specify multiple IP addresses + per server section in the configuration file. This allowed for a much more + compact configuration file (3 server sections instead of 7 in my case), + because most configuration options are identical for servers belonging to the + same ISP. It also made the output of "pdnsd-ctl status" more compact. And it + was necessary to enable a satisfactory implementation of the previous feature. + Example of the new syntax: + + ip = 123.456.789.001, 123.456.789.002, 123.456.789.003; + + At the suggestion of Greg Norris server sections no longer have to specify IP + addresses. A server section without IP addresses will remain inactive until it + is assigned one more addresses at run-time with pdnsd-ctl. + +- I've changed the implementation of dynamic arrays to make it slightly more + efficient, and improve type safety. I also got rid of several arrays of fixed + size in favor of dynamically allocated arrays. In particular, I got rid of + all occurrences of MAXPATH. I also made several static variables "automatic". + +- The output of the "status" command of pdnsd-ctl now gives more meaningful + constant names "ping|none|if|exec" instead of numbers for the "uptest" option. + I've also added some information that was previously missing. + +- I've fixed I a problem that caused pdnsd to use up a lot of CPU time and slow + down my system considerably when it received a query that took a long time to + resolve. It turned out that pdnsd can get into a "busy spin" when one of the + DNS servers pdnsd is querying refuses the connection. Apart from fixing this + bug, to speed things up additionally, I thought it would be a good idea to + mark a server down (without retesting it) after detecting errno==ECONNREFUSED. + This gives me very satisfactory performance, with the problematic server being + tried only once during every testing interval. + + New in version 1.1.11: An additional busy spin condition, triggered when a + remote server answers with "Not Implemented", has been discovered and fixed. + In case there are remaining bugs in the multiplexing code, I've added a test + that checks if the number of events reported by poll/select matches the number + of events handled by pdnsd. If not, pdnsd will log an error message and give + up. Although the bugs still need to be reported and fixed, at least this + should prevent pdnsd from wasting CPU cycles. + +- Due to a bug in Thomas' code, pdnsd tries, but fails, to remove the control + socket "pdnsd.status" before exiting. This has also been fixed. In version + 1.1.8b1-par6 I have cleaned this up some more so that pdnsd will handle + situations where it can't open or bind the control socket more gracefully. + +- I've rewritten some of the code that saves the contents of the cache to the + file "pdnsd.cache" just before pdnsd exits. This is because I noticed in my + logfiles that pdnsd occasionally had problems reading this file back at + startup. I eliminated the use of fseek() in Thomas' code. I could not find + anything that was demonstrably incorrect about his use of fseek(), but it + seemed better to me to do without it and write the file in a strictly + sequential order. Anyway, it turned out my hunch paid off: no more error + messages about "pdnsd.cache" in my logfile. + + New in version 1.1.11: I've added some new code for sorting the queue used for + purging stale cache entries. This should allow pdnsd to start up faster when + reading large cache files from disk. + +- I've extended the configuration options for policies of inclusion/exclusion + lists in server sections. The new policies options are "simple_only" and + "fqdn_only". Setting policy=simple_only will cause the server to used only for + simple hostnames if no other rule matches. On the other hand, setting + policy=fqdn_only will cause the server to be used only for fully qualified + domain names (i.e. the name has at least one dot in-between). I find these + options useful for controlling which name servers (if any) will be used by + pdnsd for simple host names. + +- I've added a new "delegation_only" option that can be used to undo the + unwanted effects of DNS "wildcards". It works roughly as the feature by the + same name in BIND. It is turned off by default. To block Verisign's + Sitefinder, add the following line to the global section of the configuration + file: + + delegation_only= com, net; + + If you find that this feature blocks some legitimate domain names, you will + probably need to add the address of a nameserver that provides good authority + information. More information can be found at + http://www.phys.uu.nl/~rombouts/pdnsd/delegationonly.html + +- It is no longer mandatory that domain names in the configuration file end in a + dot. + +- The parser for configuration files has been rewritten purely in C, so (f)lex + and yacc/bison are no longer needed to build pdnsd. + It is no longer necessary to place strings between quotes in the configuration + file, unless a string contains a special character such as whitespace, a token + that normally starts a comment, or one of the characters ",;{}". Note that + these special characters are illegal in domain names anyway. + +- New in version 1.1.11: Negating whole domains with a neg section in the + config file will result in all the subdomains being negated as well. + For example, adding the lines + + neg {name=doubleclick.com; types=domain;} + neg {name=doubleclick.net; types=domain;} + + will also negate ad.doubleclick.com, ad.fr.doubleclick.net, etc. + +- New in version 1.1.11: When running in IPv6 mode, pdnsd will now automatically + map any IPv4 addresses it reads in the config file to IPv6 addresses. + When pdnsd has been compiled with IPv6 support and runs in IPv4 mode, it will + skip IPv6 addresses with a warning message. This may result in certain server + sections becoming inactive, though. + + The -4 and -6 options should now work as advertised. + I've added two new command-line options, "-a" and "-i ". + With the -a flag pdnsd will try to detect automatically if IPv6 support is + available on a system, and fall back to IPv4 if not. The -a flag can be used + instead of -4 or -6. + The -i option can be used to specify a prefix for mapping IPv4 to IPv6 + address. The default is ::ffff.0.0.0.0. There is also a corresponding + ipv4_6_prefix= option for the config file. + +- New in version 1.1.11: I've slightly changed the way pdnsd does parallel + queries. Active queries or not canceled until we have received a useful + response from a remote name server, or all the queries have failed or timed + out. Thus the par_queries parameter is no longer the maximum number of + parallel queries, but rather the increment with which the number of parallel + queries is increased when the previous set has timed out. In the worst case + there will be pending queries to all the servers in the list of available + servers simultaneously. We may be wasting more system resources this way, but + the advantage is that we have a greater chance of catching a reply. After all, + if we wait longer anyway, why not for more servers. + I've also introduced a global timeout parameter. This is the minimum period of + time pdnsd will wait after sending the first query to a remote server before + giving up without having received a reply. The timeout options in the + configuration file are now only minimum timeout intervals. Setting the global + timeout option makes it possible to specify quite short timeout intervals in + the server sections. This will have the effect that pdnsd will start querying + additional servers fairly quickly if the first servers are slow to respond + (but will still continue to listen for responses from the first ones). This + may allow pdnsd to get an answer more quickly in certain situations. + + After receiving a reply from a remote server the server is marked up and its + time stamp is updated. This will have the effect that pdnsd doesn't bother + testing this server for availability for a period of time, and thus the + overhead caused by testing is reduced. After server timeouts, uptests are + performed by the separate server status thread, not by threads that have to + answer queries. Unresponsive servers with uptest=ping will not be marked down + immediately any more, but only after the ping test has definitely failed. + +I've also included a number of bug-fixes contained in a patch file supplied to +me by Thomas Moestl. In addition to the things I had already fixed, the +following issues are addressed: some memory leaks, dropping of root privileges +before calling uptest scripts in case pdnsd was started setuid root (which is a +bad idea anyway), passing on open fd's to uptests, integer overruns in the +status reporting code, fixing string passing from the lexer, more consistent +treatment of underscores in domain names. + +In addition to the things I've listed above, I've made various little changes to +fix minor bugs, improve efficiency or elegance, or simply to suit my my own +coding style. These changes are too numerous to list here, but some of them are +listed in the ChangeLog. Of course if you are really interested in the +nitty-gritty you can always compare the source of my version with Thomas' +original code. + +If you have any questions about the modifications I've made, you can send these +to . Questions about the original pdnsd version should +be sent to or . diff --git a/app/src/main/jni/pdnsd/THANKS b/app/src/main/jni/pdnsd/THANKS new file mode 100644 index 0000000..9c7e3bb --- /dev/null +++ b/app/src/main/jni/pdnsd/THANKS @@ -0,0 +1,66 @@ +This is a (hopefully complete) list of people I have to thank for helping me +develop and improve pdnsd: + +David G. Andersen +Andrew M. Bishop +Daniel Black +Carsten Block +Stephan Boettcher +P.J. Bostley +Rodney Brown +Kevin A. Burton +Juliusz Chroboczek +Joachim Dorner +Frank Elsner +Christian Engstler +Stefan Erhardt +Bjoern Fischer +Stefan Förster +Bert Frederiks +Mike Hammer +Jonathan Hudson +Torben Janssen +Byrial Jensen +Olaf Kirch +Nikola Kotur +Kiyo Kelvin Lee +Bernd Leibing +Patrick Loschmidt +James MacLean +Sourav K. Mandal +Fraser McCrossan +Markus Mohr +Michael Müller +Gustavo Niemeyer +Alexandre Nunes +Wolfgang Ocker +Mahesh T. Pai +Bernhard Pelz +Soenke J. Peters +Erich Reitz +Paul A. Rombouts +Brian Schroeder +Roman Shterenzon +Daniel Smolik +Milan P. Stanic +Michael Steiner +Norbert Steinl +Andreas Steinmetz +Marko Stolle +Markus Storm +Michael Ströder +Thomas Stromberg +Alan Swanson +Lyonel Vincent +Eelco Vriezekolk +Paul Wagland +Sverker Wiberg +Michael Wiedmann +Ron Yorston +Nikita V. Youshchenko +Jan-Marek Glogowski +Thomas Cort +Pierre Habouzit +Dirk Armbrust +Georg Schwarz +Ashish Shukla diff --git a/app/src/main/jni/pdnsd/TODO b/app/src/main/jni/pdnsd/TODO new file mode 100644 index 0000000..f6a1650 --- /dev/null +++ b/app/src/main/jni/pdnsd/TODO @@ -0,0 +1,20 @@ +- Implement a reference counter to ensure that newly entered records are not + purged immediately (really needed?) +- Perhaps do a two-step form of recursive query: first query those servers we + have got cached, then (if unsuccessful) look the others up and query again. + The impact of this optimisation may not be very big, because all sane servers + give A records for NS records if possible. +- Test for compatibility on other Unix-like Systems other than the BSDs and + Linux; rewrite the functions in netdev.c and icmp.c for those OSs if + necessary. Also try to get compatibility for other compilers than gcc. +- Write an install rule for the Slackware start-up script. +- Update the FAQ. +- Implement DNSSEC support. Since version 1.2.9, pdnsd is able to cache the RR + types necessary for DNSSEC, but the resolver is not yet security aware. +- Implement a lookup table (hash table) for queries in progress. This would + enable a thread that is resolving a query that is already being handled by + another thread to wait for that other thread to finish and copy its result + rather than independently query remote servers. It is very common for + resolvers to resend UDP queries if they don't get a reply within a timeout + period and if the answer is not yet cached, this will result in multiple + threads duplicating each others work in the current implementation. diff --git a/app/src/main/jni/pdnsd/acconfig.h b/app/src/main/jni/pdnsd/acconfig.h new file mode 100644 index 0000000..82b3f6e --- /dev/null +++ b/app/src/main/jni/pdnsd/acconfig.h @@ -0,0 +1,187 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +#undef ISDN_SUPPORT + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#undef ENABLE_IPV4 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET UDP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +#undef NO_UDP_QUERIES +#undef NO_TCP_QUERIES + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +#undef NO_TCP_SERVER + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#undef SRC_ADDR_DISC + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +#undef NO_POLL + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +#undef RFC2181_ME_HARDER + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +#undef RANDOM_DEVICE +#undef R_DEFAULT +#undef R_RANDOM +#undef R_ARC4RANDOM +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 5 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 16 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.62])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl +]) +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl +]) + +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_PROG_CC_C_O +# -------------- +# Like AC_PROG_CC_C_O, but changed for automake. +AC_DEFUN([AM_PROG_CC_C_O], +[AC_REQUIRE([AC_PROG_CC_C_O])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` +eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o +if test "$am_t" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +dnl Make sure AC_PROG_CC is never called again, or it will override our +dnl setting of CC. +m4_define([AC_PROG_CC], + [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])]) +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/app/src/main/jni/pdnsd/compile b/app/src/main/jni/pdnsd/compile new file mode 100644 index 0000000..1b1d232 --- /dev/null +++ b/app/src/main/jni/pdnsd/compile @@ -0,0 +1,142 @@ +#! /bin/sh +# Wrapper for compilers which do not understand `-c -o'. + +scriptversion=2005-05-14.22 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand `-c -o'. +Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file `INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; +esac + +ofile= +cfile= +eat= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as `compile cc -o foo foo.c'. + # So we strip `-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no `-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # `.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` + +# Create the lock directory. +# Note: use `[/.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/app/src/main/jni/pdnsd/config.h b/app/src/main/jni/pdnsd/config.h new file mode 100644 index 0000000..2a0f1b9 --- /dev/null +++ b/app/src/main/jni/pdnsd/config.h @@ -0,0 +1,431 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +/* #undef ISDN_SUPPORT */ + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#define ENABLE_IPV4 1 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET TCP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +/* #undef NO_UDP_QUERIES */ +/* #undef NO_TCP_QUERIES */ + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +/* #undef NO_TCP_SERVER */ + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#define SRC_ADDR_DISC 1 + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +/* #undef NO_POLL */ + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +/* #undef RFC2181_ME_HARDER */ + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +/* #undef RANDOM_DEVICE */ +#define R_DEFAULT 1 +/* #undef R_RANDOM */ +/* #undef R_ARC4RANDOM */ +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getline' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_GETLINE 1 +#endif + +/* Define to 1 if you have the `getpwnam_r' function. */ +//#define HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +//#define HAVE_MEMPCPY 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#define HAVE_MKFIFO 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if the system has the type `socklen_t'. */ +#define HAVE_SOCKLEN_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_STPCPY 1 +#endif + +/* Define to 1 if you have the `stpncpy' function. */ +//#define HAVE_STPNCPY 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef HAVE_STRLCPY */ + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if the system has the type `struct ifreq'. */ +#define HAVE_STRUCT_IFREQ 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if the system has the type `struct in_pktinfo'. */ +#define HAVE_STRUCT_IN_PKTINFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the `uname' function. */ +#define HAVE_UNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/app/src/main/jni/pdnsd/config.h.in b/app/src/main/jni/pdnsd/config.h.in new file mode 100644 index 0000000..28fb08a --- /dev/null +++ b/app/src/main/jni/pdnsd/config.h.in @@ -0,0 +1,426 @@ +/* config.h.in. Generated from configure.in by autoheader. */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +#undef ISDN_SUPPORT + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#undef ENABLE_IPV4 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET UDP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +#undef NO_UDP_QUERIES +#undef NO_TCP_QUERIES + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +#undef NO_TCP_SERVER + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#undef SRC_ADDR_DISC + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +#undef NO_POLL + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +#undef RFC2181_ME_HARDER + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +#undef RANDOM_DEVICE +#undef R_DEFAULT +#undef R_RANDOM +#undef R_ARC4RANDOM +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `getline' function. */ +#undef HAVE_GETLINE + +/* Define to 1 if you have the `getpwnam_r' function. */ +#undef HAVE_GETPWNAM_R + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if you have the `nanosleep' function. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if the system has the type `socklen_t'. */ +#undef HAVE_SOCKLEN_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `stpncpy' function. */ +#undef HAVE_STPNCPY + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if the system has the type `struct ifreq'. */ +#undef HAVE_STRUCT_IFREQ + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#undef HAVE_STRUCT_IN6_ADDR + +/* Define to 1 if the system has the type `struct in_pktinfo'. */ +#undef HAVE_STRUCT_IN_PKTINFO + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/app/src/main/jni/pdnsd/configure b/app/src/main/jni/pdnsd/configure new file mode 100644 index 0000000..7d0b877 --- /dev/null +++ b/app/src/main/jni/pdnsd/configure @@ -0,0 +1,11378 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="src" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +ALLOCA +thread_CFLAGS +RANLIB +threadlib +specbuild +def_id +cachedir +distribution +packagerelease +fullversion +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_dependency_tracking +with_distribution +with_target +with_cachedir +enable_isdn +enable_ipv4 +enable_ipv6 +enable_ipv4_startup +enable_ipv6_startup +enable_udp_queries +enable_tcp_queries +with_query_method +enable_tcp_server +enable_src_addr_disc +enable_socket_locking +enable_poll +enable_new_rrs +enable_strict_rfc2181 +with_random_device +enable_underscores +with_default_id +with_debug +with_verbosity +with_hash_buckets +enable_hash_debug +enable_rcsids +with_tcp_qtimeout +enable_tcp_subseq +with_par_queries +with_max_nameserver_ips +enable_specbuild +with_thread_lib +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-isdn Enable ISDN support (may cause problems on + some systems; only for Linux) + --disable-ipv4 Disable IPv4 networking support + (default=enabled) + --enable-ipv6 Enable IPv6 networking support + --disable-ipv4-startup Disable IPv4 on pdnsd startup by default + (default=enabled) + --enable-ipv6-startup Enable IPV6 on pdnsd startup by default + (default=IPv4) + --disable-udp-queries Disable udp as query method. + --disable-tcp-queries Disable tcp as query method. + --disable-tcp-server Disable the TCP serving ability of pdnsd + --disable-src-addr-disc Disable the UDP source address discovery + --enable-socket-locking Enable the UDP socket locking + --disable-poll Disable poll(2) and use select(2) + (default=enabled) + --disable-new-rrs Disable new DNS RR types (obsolete, currently ignored) + --enable-strict-rfc2181 Enforce strict RFC 2181 compliance + --enable-underscores Allow _ in domain names (obsolete, currently ignored) + --enable-hash-debug Debug hash tables (warning: massive output) + --enable-rcsids Enable RCS IDs in executables (obsolete, currently ignored) + --enable-tcp-subseq Enable multiple dns querys using one + tcp connection + --enable-specbuild Only used when building pdnsd from spec files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-distribution=distro Specify target distribution (default=Generic; + others: RedHat, SuSE, Debian, ArchLinux) + --with-target=platform Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin) + --with-cachedir=dir Default directory for pdnsd cache + (default=/var/cache/pdnsd) + --with-query-method=qm Specify the query method (default=udponly; + others: tcponly, tcpudp, udptcp) + --with-random-device=device Specify random device other than + /dev/random; default: C Library random() PRNG; + special value arc4random for BSD C Library + arc4random function (default on FreeBSD) + --with-default-id=id Specify default uid/gid for pdnsd + (default=nobody) + --with-debug=level Specify debugging level (0 means no debug support) + --with-verbosity=level Specify default message verbosity + --with-hash-buckets=num Number of hash buckets to use (default=1024). + The number actually used is the smallest power of two + greater or equal to the number specified here. + --with-tcp-qtimeout=secs Specify default tcp query timeout (default=30) + --with-par-queries=num Specify default parallel query number (default=2) + --with-max-nameserver-ips=num Specify maximum number of IP addresses used per nameserver obtained from NS records (default=3) + --with-thread-lib=lib Specify thread library, overriding automatic detection (for Linux only). + Possible values: LinuxThreads, + LinuxThreads2 (implements a fix for newer glibcs) + or NPTL (Native POSIX Thread Library) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +package="pdnsd" +version=`cut -d - -f 1 "$srcdir"/version` +fullversion=`cat "$srcdir"/version` +packagerelease=`cut -d - -f 2- "$srcdir"/version` + +distribution="Generic" +target="autodetect" +cachedir="/var/cache/$package" +ipv4_default=1 +have_ipv4="yes" +#newrrs="yes" +query_m="udponly" +have_tcp_server="yes" +adisc="default" +slock="no"; +def_id="nobody" +#have_rcsids="no" +udp_queries="yes" +tcp_queries="yes" +tempdir="/tmp" +randomdev=default +freebsd_pthread="4" +specbuild=no +threadlib=default + +am__api_version='1.11' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + { { $as_echo "$as_me:$LINENO: error: unsafe absolute working directory name" >&5 +$as_echo "$as_me: error: unsafe absolute working directory name" >&2;} + { (exit 1); exit 1; }; };; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + { { $as_echo "$as_me:$LINENO: error: unsafe srcdir value: \`$srcdir'" >&5 +$as_echo "$as_me: error: unsafe srcdir value: \`$srcdir'" >&2;} + { (exit 1); exit 1; }; };; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { $as_echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +$as_echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { $as_echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +$as_echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { $as_echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_STRIP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:$LINENO: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if test "${ac_cv_path_mkdir+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done +done +IFS=$as_save_IFS + +fi + + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + test -d ./--version && rmdir ./--version + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +mkdir_p="$MKDIR_P" +case $mkdir_p in + [\\/$]* | ?:[\\/]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:$LINENO: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + { { $as_echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +$as_echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE=$package + VERSION=$version + + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + + +ac_config_headers="$ac_config_headers config.h" + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:$LINENO: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:$LINENO: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + if test "${ac_cv_header_minix_config_h+set}" = set; then + { $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking minix/config.h usability" >&5 +$as_echo_n "checking minix/config.h usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking minix/config.h presence" >&5 +$as_echo_n "checking minix/config.h presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_header_minix_config_h=$ac_header_preproc +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } + +fi +if test "x$ac_cv_header_minix_config_h" = x""yes; then + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_1_SOURCE 2 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _MINIX 1 +_ACEOF + + fi + + + + { $as_echo "$as_me:$LINENO: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if test "${ac_cv_safe_to_define___extensions__+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_safe_to_define___extensions__=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_safe_to_define___extensions__=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + cat >>confdefs.h <<\_ACEOF +#define __EXTENSIONS__ 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _GNU_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _POSIX_PTHREAD_SEMANTICS 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _TANDEM_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$fullversion" +_ACEOF + + + + + +# Check whether --with-distribution was given. +if test "${with_distribution+set}" = set; then + withval=$with_distribution; distribution=$withval +fi + + + + +# Check whether --with-target was given. +if test "${with_target+set}" = set; then + withval=$with_target; target=$withval +fi + +case $target in + Linux|linux) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_LINUX +_ACEOF + + target="Linux" + ;; + BSD| bsd) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_BSD +_ACEOF + + target="BSD" + ;; + Cygwin|CYGWIN|cygwin) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_CYGWIN +_ACEOF + + target="cygwin" + ;; + autodetect) + ;; + *) + { { $as_echo "$as_me:$LINENO: error: --with-target must have Linux, BSD or Cygwin as parameter." >&5 +$as_echo "$as_me: error: --with-target must have Linux, BSD or Cygwin as parameter." >&2;} + { (exit 1); exit 1; }; } + ;; +esac + + +# Check whether --enable-isdn was given. +if test "${enable_isdn+set}" = set; then + enableval=$enable_isdn; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define ISDN_SUPPORT 1 +_ACEOF + +fi + + +# Check whether --enable-ipv4 was given. +if test "${enable_ipv4+set}" = set; then + enableval=$enable_ipv4; have_ipv4=$enableval +fi + + +test $have_ipv4 = "yes" && cat >>confdefs.h <<\_ACEOF +#define ENABLE_IPV4 1 +_ACEOF + + +# Check whether --enable-ipv6 was given. +if test "${enable_ipv6+set}" = set; then + enableval=$enable_ipv6; if test $enableval = "yes" ; then + cat >>confdefs.h <<\_ACEOF +#define ENABLE_IPV6 1 +_ACEOF + + if test $have_ipv4 != "yes" ; then + ipv4_default=0 + fi + fi +fi + + +# Check whether --enable-ipv4-startup was given. +if test "${enable_ipv4_startup+set}" = set; then + enableval=$enable_ipv4_startup; if test $enableval = "yes" ; then + ipv4_default=1 + else + ipv4_default=0 + fi +fi + + +# Check whether --enable-ipv6-startup was given. +if test "${enable_ipv6_startup+set}" = set; then + enableval=$enable_ipv6_startup; if test $enableval = "yes" ; then + ipv4_default=0 + else + ipv4_default=1 + fi +fi + + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_IPV4 $ipv4_default +_ACEOF + + +# Check whether --enable-udp-queries was given. +if test "${enable_udp_queries+set}" = set; then + enableval=$enable_udp_queries; udp_queries=$enableval +fi + + +# Check whether --enable-tcp-queries was given. +if test "${enable_tcp_queries+set}" = set; then + enableval=$enable_tcp_queries; tcp_queries=$enableval +fi + + + +# Check whether --with-query-method was given. +if test "${with_query_method+set}" = set; then + withval=$with_query_method; query_m=$withval +fi + +case $query_m in + udponly|UDPonly) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET UDP_ONLY +_ACEOF + + udp_queries=yes; + ;; + tcponly|TCPonly) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET TCP_ONLY +_ACEOF + + tcp_queries=yes; + ;; + tcpudp|TCPUDP) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET TCP_UDP +_ACEOF + + udp_queries=yes; + tcp_queries=yes; + ;; + udptcp|UDPTCP) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET UDP_TCP +_ACEOF + + udp_queries=yes; + tcp_queries=yes; + ;; + *) + { { $as_echo "$as_me:$LINENO: error: --with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter." >&5 +$as_echo "$as_me: error: --with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter." >&2;} + { (exit 1); exit 1; }; } + ;; +esac + +test $udp_queries != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_UDP_QUERIES 1 +_ACEOF + +test $tcp_queries != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_TCP_QUERIES 1 +_ACEOF + + +# Check whether --enable-tcp-server was given. +if test "${enable_tcp_server+set}" = set; then + enableval=$enable_tcp_server; have_tcp_server=$enableval +fi + + +test $have_tcp_server != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_TCP_SERVER 1 +_ACEOF + + +# Check whether --enable-src-addr-disc was given. +if test "${enable_src_addr_disc+set}" = set; then + enableval=$enable_src_addr_disc; adisc=$enableval +fi + + +# Check whether --enable-socket-locking was given. +if test "${enable_socket_locking+set}" = set; then + enableval=$enable_socket_locking; slock=$enableval +fi + + +test $slock = "yes" && cat >>confdefs.h <<\_ACEOF +#define SOCKET_LOCKING 1 +_ACEOF + + +# Check whether --enable-poll was given. +if test "${enable_poll+set}" = set; then + enableval=$enable_poll; test $enableval != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_POLL 1 +_ACEOF + +fi + + +# Check whether --enable-new-rrs was given. +if test "${enable_new_rrs+set}" = set; then + enableval=$enable_new_rrs; newrrs=$enableval +fi + + +# Check whether --enable-strict-rfc2181 was given. +if test "${enable_strict_rfc2181+set}" = set; then + enableval=$enable_strict_rfc2181; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define RFC2181_ME_HARDER 1 +_ACEOF + +fi + + + +# Check whether --with-random-device was given. +if test "${with_random_device+set}" = set; then + withval=$with_random_device; randomdev=$withval +fi + + +if test "$randomdev" = arc4random ; then + cat >>confdefs.h <<\_ACEOF +#define R_ARC4RANDOM 1 +_ACEOF + +elif test "$randomdev" = random ; then + cat >>confdefs.h <<\_ACEOF +#define R_RANDOM 1 +_ACEOF + +elif test "$randomdev" = default ; then + cat >>confdefs.h <<\_ACEOF +#define R_DEFAULT 1 +_ACEOF + +else + cat >>confdefs.h <<_ACEOF +#define RANDOM_DEVICE "$randomdev" +_ACEOF + +fi + +# Check whether --enable-underscores was given. +if test "${enable_underscores+set}" = set; then + enableval=$enable_underscores; underscores=$enableval +fi + + + +# Check whether --with-default-id was given. +if test "${with_default_id+set}" = set; then + withval=$with_default_id; def_id=$withval +fi + + + + +# Check whether --with-debug was given. +if test "${with_debug+set}" = set; then + withval=$with_debug; cat >>confdefs.h <<_ACEOF +#define DEBUG $withval +_ACEOF + +fi + + + +# Check whether --with-verbosity was given. +if test "${with_verbosity+set}" = set; then + withval=$with_verbosity; cat >>confdefs.h <<_ACEOF +#define VERBOSITY $withval +_ACEOF + +fi + + + +# Check whether --with-hash-buckets was given. +if test "${with_hash_buckets+set}" = set; then + withval=$with_hash_buckets; powof2=1 + hashsz=0 + + while test $powof2 -lt "$withval" + do + powof2=`expr 2 '*' $powof2` + hashsz=`expr $hashsz '+' 1` + done + cat >>confdefs.h <<_ACEOF +#define HASH_SZ $hashsz +_ACEOF + + +fi + + +# Check whether --enable-hash-debug was given. +if test "${enable_hash_debug+set}" = set; then + enableval=$enable_hash_debug; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define DEBUG_HASH 1 +_ACEOF + +fi + + +# Check whether --enable-rcsids was given. +if test "${enable_rcsids+set}" = set; then + enableval=$enable_rcsids; have_rcsids=$enableval +fi + + + +# Check whether --with-tcp-qtimeout was given. +if test "${with_tcp_qtimeout+set}" = set; then + withval=$with_tcp_qtimeout; cat >>confdefs.h <<_ACEOF +#define TCP_TIMEOUT $withval +_ACEOF + +fi + + +# Check whether --enable-tcp-subseq was given. +if test "${enable_tcp_subseq+set}" = set; then + enableval=$enable_tcp_subseq; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define TCP_SUBSEQ 1 +_ACEOF + +fi + + + +# Check whether --with-par-queries was given. +if test "${with_par_queries+set}" = set; then + withval=$with_par_queries; cat >>confdefs.h <<_ACEOF +#define PAR_QUERIES $withval +_ACEOF + +fi + + + +# Check whether --with-max-nameserver-ips was given. +if test "${with_max_nameserver_ips+set}" = set; then + withval=$with_max_nameserver_ips; cat >>confdefs.h <<_ACEOF +#define MAXNAMESERVIPS $withval +_ACEOF + +fi + + +# Check whether --enable-specbuild was given. +if test "${enable_specbuild+set}" = set; then + enableval=$enable_specbuild; specbuild=$enableval +fi + + + + + +# Check whether --with-thread-lib was given. +if test "${with_thread_lib+set}" = set; then + withval=$with_thread_lib; threadlib=$withval +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc + +if test "x$CC" != xcc; then + { $as_echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 +$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; } +else + { $as_echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 +$as_echo_n "checking whether cc understands -c and -o together... " >&6; } +fi +set dummy $CC; ac_cc=`$as_echo "$2" | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if { as_var=ac_cv_prog_cc_${ac_cc}_c_o; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5' +rm -f conftest2.* +if { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest2.$ac_objext && { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5' + rm -f conftest2.* + if { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest2.$ac_objext && { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f core conftest* + +fi +if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define NO_MINUS_C_MINUS_O 1 +_ACEOF + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o +if test "$am_t" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + +if test "$target" = "autodetect" ; then + { $as_echo "$as_me:$LINENO: checking for autodetect build target" >&5 +$as_echo_n "checking for autodetect build target... " >&6; } + uname_sys=`uname` + if test $? -ne 0 ; then + { $as_echo "$as_me:$LINENO: result: failed" >&5 +$as_echo "failed" >&6; } + { { $as_echo "$as_me:$LINENO: error: uname failed or was not found in path" >&5 +$as_echo "$as_me: error: uname failed or was not found in path" >&2;} + { (exit 1); exit 1; }; } + else + case "${uname_sys}" in + Linux) + { $as_echo "$as_me:$LINENO: result: Linux" >&5 +$as_echo "Linux" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_LINUX +_ACEOF + + target="Linux" + ;; + FreeBSD|NetBSD|OpenBSD|Darwin) + { $as_echo "$as_me:$LINENO: result: \"${uname_sys}\"" >&5 +$as_echo "\"${uname_sys}\"" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_BSD +_ACEOF + + target="BSD" + ;; + CYGWIN*) + { $as_echo "$as_me:$LINENO: result: \"${uname_sys}\"" >&5 +$as_echo "\"${uname_sys}\"" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_CYGWIN +_ACEOF + + target="cygwin" + ;; + *) + { $as_echo "$as_me:$LINENO: result: failed" >&5 +$as_echo "failed" >&6; } + { { $as_echo "$as_me:$LINENO: error: Your system type could not be identified. Try setting it manually using +--with-target" >&5 +$as_echo "$as_me: error: Your system type could not be identified. Try setting it manually using +--with-target" >&2;} + { (exit 1); exit 1; }; } + ;; + esac + fi +fi + +#if test "$target" = BSD ; then +# uname_sys=`uname` +# if test "$uname_sys" = FreeBSD ; then +# AC_MSG_CHECKING([for FreeBSD version]) +# osrel=`sysctl -n kern.osreldate` +# if test $osrel -ge 500016 ; then +# AC_MSG_RESULT([5.0 (>= 500016)]) +# freebsd_pthread="5" +# else +# AC_MSG_RESULT([<=5.0 (< 500016)]) +# freebsd_pthread="4" +# fi +# fi +#fi + +if test "$adisc" = "default"; then + if test "$target" = "cygwin" ; then +# Don't do UDP source address discovery on Cygwin platform by default. + adisc="no" + else + adisc="yes" + fi +fi + +test "$adisc" = "yes" && cat >>confdefs.h <<\_ACEOF +#define SRC_ADDR_DISC 1 +_ACEOF + + + +if test "$target" = "Linux"; then +{ $as_echo "$as_me:$LINENO: checking if we can compile and link with -pthread" >&5 +$as_echo_n "checking if we can compile and link with -pthread... " >&6; } +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -pthread" +cat >conftest.$ac_ext <<_ACEOF + +#include + +void *thread_func(void *data) +{ + *((int *)data)=1; + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int val; + + if(pthread_create(&thread, NULL, thread_func, &val)) + return 1; + + if(pthread_join(thread,&retval)) + return 1; + + return (*((int *)retval)!=1); +} + +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + gcc_pthread_flag="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + gcc_pthread_flag="no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +CFLAGS="$old_CFLAGS" +{ $as_echo "$as_me:$LINENO: result: $gcc_pthread_flag" >&5 +$as_echo "$gcc_pthread_flag" >&6; } + + if test "$gcc_pthread_flag" = yes ; then + thread_CFLAGS="-pthread" + + else + +{ $as_echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_pthread_pthread_create=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_pthread_pthread_create=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +fi + + fi +fi +if test "$target" = "BSD" -a `uname` != Darwin ; then +# if test $freebsd_pthread = 4 ; then + thread_CFLAGS="-pthread" + +# else +# AC_CHECK_LIB(c_r, pthread_create, , +# AC_MSG_ERROR([You must have libc_r installed to build/run pdnsd!])) +# fi; +fi + +if test "$target" = "Linux" -a "$threadlib" = default; then +{ $as_echo "$as_me:$LINENO: checking if this is an NPTL-based system" >&5 +$as_echo_n "checking if this is an NPTL-based system... " >&6; } +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $thread_CFLAGS" +if test "$cross_compiling" = yes; then + + { $as_echo "$as_me:$LINENO: result: couldn't run test program" >&5 +$as_echo "couldn't run test program" >&6; } + threadlib=linuxthreads + +else + cat >conftest.$ac_ext <<_ACEOF + +#include +#include +#include +#include +#include +#include + +/* All this function does is return its PID (in a roundabout way). */ +void *thread_func(void *data) +{ + *((int *)data)=getpid(); + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int err,mainpid,thrdpid; + + err=pthread_create(&thread, NULL, thread_func, &thrdpid); + if(err) { + fprintf(stderr,"pthread_create failed: %s\n",strerror(err)); + return 1; + } + err=pthread_join(thread,&retval); + if(err) { + fprintf(stderr,"pthread_join failed: %s\n",strerror(err)); + return 1; + } + mainpid=getpid(); + /* In LinuxThreads implementations, the pids of the threads will usually differ + in a non Posix-compliant way. */ + fprintf(stderr,"main pid=%d, thread pid=%d\n",mainpid,*((int *)retval)); + return (*((int *)retval)!=mainpid); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + threadlib=nptl + +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) + + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + threadlib=linuxthreads + +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +CFLAGS="$old_CFLAGS" +fi + +if test "$threadlib" = nptl -o "$threadlib" = NPTL; then + cat >>confdefs.h <<\_ACEOF +#define THREADLIB_NPTL 1 +_ACEOF + +elif test "$threadlib" = linuxthreads2 -o "$threadlib" = LinuxThreads2 -o "$threadlib" = lt2; then + cat >>confdefs.h <<\_ACEOF +#define THREADLIB_LINUXTHREADS2 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + +for ac_header in fcntl.h malloc.h sys/ioctl.h sys/time.h syslog.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + +for ac_header in sys/types.h sys/socket.h net/if.h netinet/in.h sys/poll.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +{ $as_echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if test "${ac_cv_c_const+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_const=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const /**/ +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for pid_t" >&5 +$as_echo_n "checking for pid_t... " >&6; } +if test "${ac_cv_type_pid_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_pid_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((pid_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_pid_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +$as_echo "$ac_cv_type_pid_t" >&6; } +if test "x$ac_cv_type_pid_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for size_t" >&5 +$as_echo_n "checking for size_t... " >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_size_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((size_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +$as_echo "$ac_cv_type_size_t" >&6; } +if test "x$ac_cv_type_size_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if test "${ac_cv_header_time+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_time=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_time=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if test "${ac_cv_struct_tm+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_struct_tm=time.h +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_struct_tm=sys/time.h +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +$as_echo "$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for socklen_t" >&5 +$as_echo_n "checking for socklen_t... " >&6; } +if test "${ac_cv_type_socklen_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_socklen_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (socklen_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((socklen_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_socklen_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_socklen_t" >&5 +$as_echo "$ac_cv_type_socklen_t" >&6; } +if test "x$ac_cv_type_socklen_t" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_SOCKLEN_T 1 +_ACEOF + + +else + cat >>confdefs.h <<\_ACEOF +#define socklen_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for struct in6_addr" >&5 +$as_echo_n "checking for struct in6_addr... " >&6; } +if test "${ac_cv_type_struct_in6_addr+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_in6_addr=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct in6_addr)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct in6_addr))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_in6_addr=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_in6_addr" >&5 +$as_echo "$ac_cv_type_struct_in6_addr" >&6; } +if test "x$ac_cv_type_struct_in6_addr" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IN6_ADDR 1 +_ACEOF + + +fi +{ $as_echo "$as_me:$LINENO: checking for struct in_pktinfo" >&5 +$as_echo_n "checking for struct in_pktinfo... " >&6; } +if test "${ac_cv_type_struct_in_pktinfo+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_in_pktinfo=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct in_pktinfo)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct in_pktinfo))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_in_pktinfo=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_in_pktinfo" >&5 +$as_echo "$ac_cv_type_struct_in_pktinfo" >&6; } +if test "x$ac_cv_type_struct_in_pktinfo" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IN_PKTINFO 1 +_ACEOF + + +fi +{ $as_echo "$as_me:$LINENO: checking for struct ifreq" >&5 +$as_echo_n "checking for struct ifreq... " >&6; } +if test "${ac_cv_type_struct_ifreq+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_ifreq=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct ifreq)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct ifreq))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_ifreq=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_ifreq" >&5 +$as_echo "$ac_cv_type_struct_ifreq" >&6; } +if test "x$ac_cv_type_struct_ifreq" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IFREQ 1 +_ACEOF + + +fi + + +{ $as_echo "$as_me:$LINENO: checking for CPP C99 Variadic macro support" >&5 +$as_echo_n "checking for CPP C99 Variadic macro support... " >&6; } +cat >conftest.$ac_ext <<_ACEOF + +#define a(...) junk(0,__VA_ARGS__) +extern void junk(int i,...); +int main() +{ + a(0); + a("a"); + a(0, "a", 1); + return 0; +} + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cpp_c99_variadic="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + cpp_c99_variadic="no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $cpp_c99_variadic" >&5 +$as_echo "$cpp_c99_variadic" >&6; } +if test $cpp_c99_variadic = yes; then + cat >>confdefs.h <<\_ACEOF +#define CPP_C99_VARIADIC_MACROS 1 +_ACEOF + +else + if test "$GCC" != yes; then + { { $as_echo "$as_me:$LINENO: error: Compiler must support C99 or gcc variadic macros" >&5 +$as_echo "$as_me: error: Compiler must support C99 or gcc variadic macros" >&2;} + { (exit 1); exit 1; }; } + fi; +fi + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +{ $as_echo "$as_me:$LINENO: checking for working alloca.h" >&5 +$as_echo_n "checking for working alloca.h... " >&6; } +if test "${ac_cv_working_alloca_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + if (p) return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_working_alloca_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_working_alloca_h=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5 +$as_echo "$ac_cv_working_alloca_h" >&6; } +if test $ac_cv_working_alloca_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA_H 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for alloca" >&5 +$as_echo_n "checking for alloca... " >&6; } +if test "${ac_cv_func_alloca_works+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + if (p) return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func_alloca_works=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func_alloca_works=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5 +$as_echo "$ac_cv_func_alloca_works" >&6; } + +if test $ac_cv_func_alloca_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA 1 +_ACEOF + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=\${LIBOBJDIR}alloca.$ac_objext + +cat >>confdefs.h <<\_ACEOF +#define C_ALLOCA 1 +_ACEOF + + +{ $as_echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5 +$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; } +if test "${ac_cv_os_cray+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined CRAY && ! defined CRAY2 +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5 +$as_echo "$ac_cv_os_cray" >&6; } +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +{ $as_echo "$as_me:$LINENO: checking stack direction for C alloca" >&5 +$as_echo_n "checking stack direction for C alloca... " >&6; } +if test "${ac_cv_c_stack_direction+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} + +int +main () +{ + return find_stack_direction () < 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_stack_direction=1 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_stack_direction=-1 +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 +$as_echo "$ac_cv_c_stack_direction" >&6; } + +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + { $as_echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +$as_echo_n "checking whether $CC needs -traditional... " >&6; } +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +$as_echo "$ac_cv_prog_gcc_traditional" >&6; } + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +{ $as_echo "$as_me:$LINENO: checking for working memcmp" >&5 +$as_echo_n "checking for working memcmp... " >&6; } +if test "${ac_cv_func_memcmp_working+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +$as_echo "$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + + +{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +for ac_func in vprintf +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +{ $as_echo "$as_me:$LINENO: checking for _doprnt" >&5 +$as_echo_n "checking for _doprnt... " >&6; } +if test "${ac_cv_func__doprnt+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub__doprnt || defined __stub____doprnt +choke me +#endif + +int +main () +{ +return _doprnt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func__doprnt=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func__doprnt=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +$as_echo "$ac_cv_func__doprnt" >&6; } +if test "x$ac_cv_func__doprnt" = x""yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + +{ $as_echo "$as_me:$LINENO: checking for library containing nanosleep" >&5 +$as_echo_n "checking for library containing nanosleep... " >&6; } +if test "${ac_cv_search_nanosleep+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char nanosleep (); +int +main () +{ +return nanosleep (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_nanosleep=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_nanosleep+set}" = set; then + break +fi +done +if test "${ac_cv_search_nanosleep+set}" = set; then + : +else + ac_cv_search_nanosleep=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_nanosleep" >&5 +$as_echo "$ac_cv_search_nanosleep" >&6; } +ac_res=$ac_cv_search_nanosleep +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:$LINENO: checking for library containing socket" >&5 +$as_echo_n "checking for library containing socket... " >&6; } +if test "${ac_cv_search_socket+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +for ac_lib in '' socket; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib -lnsl $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_socket=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_socket+set}" = set; then + break +fi +done +if test "${ac_cv_search_socket+set}" = set; then + : +else + ac_cv_search_socket=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_socket" >&5 +$as_echo "$ac_cv_search_socket" >&6; } +ac_res=$ac_cv_search_socket +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:$LINENO: checking for library containing inet_aton" >&5 +$as_echo_n "checking for library containing inet_aton... " >&6; } +if test "${ac_cv_search_inet_aton+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char inet_aton (); +int +main () +{ +return inet_aton (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_inet_aton=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_inet_aton+set}" = set; then + break +fi +done +if test "${ac_cv_search_inet_aton+set}" = set; then + : +else + ac_cv_search_inet_aton=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_inet_aton" >&5 +$as_echo "$ac_cv_search_inet_aton" >&6; } +ac_res=$ac_cv_search_inet_aton +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + + + + + + + + + +for ac_func in nanosleep gettimeofday mkfifo select socket strerror uname snprintf vsnprintf +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + true +else + { { $as_echo "$as_me:$LINENO: error: One of the functions required for pdnsd were not found." >&5 +$as_echo "$as_me: error: One of the functions required for pdnsd were not found." >&2;} + { (exit 1); exit 1; }; } +fi +done + + +for ac_func in poll +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + true +else + cat >>confdefs.h <<\_ACEOF +#define NO_POLL 1 +_ACEOF + +fi +done + + + + + + + + + + + + + +for ac_func in strdup strndup stpcpy stpncpy strlcpy mempcpy getline asprintf vasprintf getpwnam_r inet_ntop inet_pton +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +ac_config_files="$ac_config_files pdnsd.spec Makefile file-list.base contrib/Makefile doc/Makefile doc/pdnsd.8 doc/pdnsd.conf.5 doc/pdnsd.conf src/Makefile src/pdnsd-ctl/Makefile src/rc/Makefile src/rc/RedHat/Makefile src/rc/RedHat/pdnsd src/rc/SuSE/Makefile src/rc/SuSE/pdnsd src/rc/Debian/Makefile src/rc/Debian/pdnsd src/rc/Slackware/Makefile src/rc/Slackware/rc.pdnsd src/rc/ArchLinux/Makefile src/rc/ArchLinux/pdnsd src/test/Makefile PKGBUILD" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { $as_echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "pdnsd.spec") CONFIG_FILES="$CONFIG_FILES pdnsd.spec" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "file-list.base") CONFIG_FILES="$CONFIG_FILES file-list.base" ;; + "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/pdnsd.8") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.8" ;; + "doc/pdnsd.conf.5") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.conf.5" ;; + "doc/pdnsd.conf") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.conf" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/pdnsd-ctl/Makefile") CONFIG_FILES="$CONFIG_FILES src/pdnsd-ctl/Makefile" ;; + "src/rc/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Makefile" ;; + "src/rc/RedHat/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/RedHat/Makefile" ;; + "src/rc/RedHat/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/RedHat/pdnsd" ;; + "src/rc/SuSE/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/SuSE/Makefile" ;; + "src/rc/SuSE/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/SuSE/pdnsd" ;; + "src/rc/Debian/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Debian/Makefile" ;; + "src/rc/Debian/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/Debian/pdnsd" ;; + "src/rc/Slackware/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Slackware/Makefile" ;; + "src/rc/Slackware/rc.pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/Slackware/rc.pdnsd" ;; + "src/rc/ArchLinux/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/ArchLinux/Makefile" ;; + "src/rc/ArchLinux/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/ArchLinux/pdnsd" ;; + "src/test/Makefile") CONFIG_FILES="$CONFIG_FILES src/test/Makefile" ;; + "PKGBUILD") CONFIG_FILES="$CONFIG_FILES PKGBUILD" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=' ' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5 +$as_echo "$as_me: error: could not setup config headers machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5 +$as_echo "$as_me: error: could not create -" >&2;} + { (exit 1); exit 1; }; } + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:$LINENO: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir=$dirpart/$fdir + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + + esac +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/app/src/main/jni/pdnsd/configure.in b/app/src/main/jni/pdnsd/configure.in new file mode 100644 index 0000000..872394c --- /dev/null +++ b/app/src/main/jni/pdnsd/configure.in @@ -0,0 +1,541 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(src) + +package="pdnsd" +version=`cut -d - -f 1 "$srcdir"/version` +fullversion=`cat "$srcdir"/version` +packagerelease=`cut -d - -f 2- "$srcdir"/version` + +distribution="Generic" +target="autodetect" +cachedir="/var/cache/$package" +ipv4_default=1 +have_ipv4="yes" +#newrrs="yes" +query_m="udponly" +have_tcp_server="yes" +adisc="default" +slock="no"; +def_id="nobody" +#have_rcsids="no" +udp_queries="yes" +tcp_queries="yes" +tempdir="/tmp" +randomdev=default +freebsd_pthread="4" +specbuild=no +threadlib=default + +AM_INIT_AUTOMAKE($package, $version, [no-define]) +AM_CONFIG_HEADER(config.h) +AC_GNU_SOURCE +AC_DEFINE_UNQUOTED(VERSION,"$fullversion") +AC_SUBST(fullversion) +AC_SUBST(packagerelease) + +AC_ARG_WITH(distribution, +[ --with-distribution=distro Specify target distribution (default=Generic; + others: RedHat, SuSE, Debian, ArchLinux)], + distribution=$withval) +AC_SUBST(distribution) + +AC_ARG_WITH(target, +[ --with-target=platform Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin)], + target=$withval) +case $target in + Linux|linux) + AC_DEFINE(TARGET, TARGET_LINUX) + target="Linux" + ;; + BSD| bsd) + AC_DEFINE(TARGET, TARGET_BSD) + target="BSD" + ;; + Cygwin|CYGWIN|cygwin) + AC_DEFINE(TARGET, TARGET_CYGWIN) + target="cygwin" + ;; + autodetect) + ;; + *) + AC_MSG_ERROR([--with-target must have Linux, BSD or Cygwin as parameter.]) + ;; +esac + +AC_ARG_ENABLE(isdn, +[ --enable-isdn Enable ISDN support (may cause problems on + some systems; only for Linux)], + test $enableval = "yes" && AC_DEFINE(ISDN_SUPPORT)) + +AC_ARG_ENABLE(ipv4, +[ --disable-ipv4 Disable IPv4 networking support + (default=enabled)], + have_ipv4=$enableval) + +test $have_ipv4 = "yes" && AC_DEFINE(ENABLE_IPV4) + +AC_ARG_ENABLE(ipv6, +[ --enable-ipv6 Enable IPv6 networking support], +[ if test $enableval = "yes" ; then + AC_DEFINE(ENABLE_IPV6) + if test $have_ipv4 != "yes" ; then + ipv4_default=0 + fi + fi]) + +AC_ARG_ENABLE(ipv4-startup, +[ --disable-ipv4-startup Disable IPv4 on pdnsd startup by default + (default=enabled)], +[ if test $enableval = "yes" ; then + ipv4_default=1 + else + ipv4_default=0 + fi]) + +AC_ARG_ENABLE(ipv6-startup, +[ --enable-ipv6-startup Enable IPV6 on pdnsd startup by default + (default=IPv4)], +[ if test $enableval = "yes" ; then + ipv4_default=0 + else + ipv4_default=1 + fi]) + +AC_DEFINE_UNQUOTED(DEFAULT_IPV4, $ipv4_default) + +AC_ARG_ENABLE(udp-queries, +[ --disable-udp-queries Disable udp as query method.], + udp_queries=$enableval) + +AC_ARG_ENABLE(tcp-queries, +[ --disable-tcp-queries Disable tcp as query method.], + tcp_queries=$enableval) + +AC_ARG_WITH(query-method, +[ --with-query-method=qm Specify the query method (default=udponly; + others: tcponly, tcpudp, udptcp)], + query_m=$withval) +case $query_m in + udponly|UDPonly) + AC_DEFINE(M_PRESET, UDP_ONLY) + udp_queries=yes; + ;; + tcponly|TCPonly) + AC_DEFINE(M_PRESET, TCP_ONLY) + tcp_queries=yes; + ;; + tcpudp|TCPUDP) + AC_DEFINE(M_PRESET, TCP_UDP) + udp_queries=yes; + tcp_queries=yes; + ;; + udptcp|UDPTCP) + AC_DEFINE(M_PRESET, UDP_TCP) + udp_queries=yes; + tcp_queries=yes; + ;; + *) + AC_MSG_ERROR([--with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter.]) + ;; +esac + +test $udp_queries != "yes" && AC_DEFINE(NO_UDP_QUERIES) +test $tcp_queries != "yes" && AC_DEFINE(NO_TCP_QUERIES) + +AC_ARG_ENABLE(tcp-server, +[ --disable-tcp-server Disable the TCP serving ability of pdnsd], + have_tcp_server=$enableval) + +test $have_tcp_server != "yes" && AC_DEFINE(NO_TCP_SERVER) + +AC_ARG_ENABLE(src-addr-disc, +[ --disable-src-addr-disc Disable the UDP source address discovery], + adisc=$enableval) + +AC_ARG_ENABLE(socket-locking, +[ --enable-socket-locking Enable the UDP socket locking], + slock=$enableval) + +test $slock = "yes" && AC_DEFINE(SOCKET_LOCKING) + +AC_ARG_ENABLE(poll, +[ --disable-poll Disable poll(2) and use select(2) + (default=enabled)], + test $enableval != "yes" && AC_DEFINE(NO_POLL)) + +AC_ARG_ENABLE(new-rrs, +[ --disable-new-rrs Disable new DNS RR types (obsolete, currently ignored)], + newrrs=$enableval) + +AC_ARG_ENABLE(strict-rfc2181, +[ --enable-strict-rfc2181 Enforce strict RFC 2181 compliance], + test $enableval = "yes" && AC_DEFINE(RFC2181_ME_HARDER)) + +AC_ARG_WITH(random-device, +[ --with-random-device=device Specify random device other than + /dev/random; default: C Library random() PRNG; + special value arc4random for BSD C Library + arc4random function (default on FreeBSD)], + randomdev=$withval) + +if test "$randomdev" = arc4random ; then + AC_DEFINE(R_ARC4RANDOM) +elif test "$randomdev" = random ; then + AC_DEFINE(R_RANDOM) +elif test "$randomdev" = default ; then + AC_DEFINE(R_DEFAULT) +else + AC_DEFINE_UNQUOTED(RANDOM_DEVICE, "$randomdev") +fi + +AC_ARG_ENABLE(underscores, +[ --enable-underscores Allow _ in domain names (obsolete, currently ignored)], + underscores=$enableval) + +AC_ARG_WITH(default-id, +[ --with-default-id=id Specify default uid/gid for pdnsd + (default=nobody)], + def_id=$withval) +AC_SUBST(def_id) + +AC_ARG_WITH(debug, +[ --with-debug=level Specify debugging level (0 means no debug support)], + AC_DEFINE_UNQUOTED(DEBUG, $withval)) + +AC_ARG_WITH(verbosity, +[ --with-verbosity=level Specify default message verbosity], + AC_DEFINE_UNQUOTED(VERBOSITY, $withval)) + +AC_ARG_WITH(hash-buckets, +[ --with-hash-buckets=num Number of hash buckets to use (default=1024). + The number actually used is the smallest power of two + greater or equal to the number specified here.], + powof2=1 + hashsz=0 + + while test $powof2 -lt "$withval" + do + powof2=`expr 2 '*' $powof2` + hashsz=`expr $hashsz '+' 1` + done + AC_DEFINE_UNQUOTED(HASH_SZ, $hashsz) +) + +AC_ARG_ENABLE(hash-debug, +[ --enable-hash-debug Debug hash tables (warning: massive output)], + test $enableval = "yes" && AC_DEFINE(DEBUG_HASH)) + +AC_ARG_ENABLE(rcsids, +[ --enable-rcsids Enable RCS IDs in executables (obsolete, currently ignored)], + have_rcsids=$enableval) + +AC_ARG_WITH(tcp-qtimeout, +[ --with-tcp-qtimeout=secs Specify default tcp query timeout (default=30)], + AC_DEFINE_UNQUOTED(TCP_TIMEOUT, $withval)) + +AC_ARG_ENABLE(tcp-subseq, +[ --enable-tcp-subseq Enable multiple dns querys using one + tcp connection], + test $enableval = "yes" && AC_DEFINE(TCP_SUBSEQ)) + +AC_ARG_WITH(par-queries, +[ --with-par-queries=num Specify default parallel query number (default=2)], + AC_DEFINE_UNQUOTED(PAR_QUERIES, $withval)) + +AC_ARG_WITH(max-nameserver-ips, +[ --with-max-nameserver-ips=num Specify maximum number of IP addresses used per nameserver obtained from NS records (default=3)], + AC_DEFINE_UNQUOTED(MAXNAMESERVIPS, $withval)) + +AC_ARG_ENABLE(specbuild, +[ --enable-specbuild Only used when building pdnsd from spec files], + specbuild=$enableval) + +AC_SUBST(specbuild) + +AC_ARG_WITH(thread-lib, +[ --with-thread-lib=lib Specify thread library, overriding automatic detection (for Linux only). + Possible values: LinuxThreads, + LinuxThreads2 (implements a fix for newer glibcs) + or NPTL (Native POSIX Thread Library)], + threadlib=$withval) + +AC_SUBST(threadlib) + +dnl Checks for programs. +AC_PROG_CC +AM_PROG_CC_STDC +AM_PROG_CC_C_O +AC_PROG_INSTALL + +dnl For dbm subsystem libraries +AC_PROG_RANLIB + +if test "$target" = "autodetect" ; then + AC_MSG_CHECKING([for autodetect build target]) + uname_sys=`uname` + if test $? -ne 0 ; then + AC_MSG_RESULT([failed]) + AC_MSG_ERROR([uname failed or was not found in path]) + else + case "${uname_sys}" in + Linux) + AC_MSG_RESULT(Linux) + AC_DEFINE(TARGET, TARGET_LINUX) + target="Linux" + ;; + FreeBSD|NetBSD|OpenBSD|Darwin) + AC_MSG_RESULT("${uname_sys}") + AC_DEFINE(TARGET, TARGET_BSD) + target="BSD" + ;; + CYGWIN*) + AC_MSG_RESULT("${uname_sys}") + AC_DEFINE(TARGET, TARGET_CYGWIN) + target="cygwin" + ;; + *) + AC_MSG_RESULT([failed]) + AC_MSG_ERROR( +[Your system type could not be identified. Try setting it manually using +--with-target]) + ;; + esac + fi +fi + +#if test "$target" = BSD ; then +# uname_sys=`uname` +# if test "$uname_sys" = FreeBSD ; then +# AC_MSG_CHECKING([for FreeBSD version]) +# osrel=`sysctl -n kern.osreldate` +# if test $osrel -ge 500016 ; then +# AC_MSG_RESULT([5.0 (>= 500016)]) +# freebsd_pthread="5" +# else +# AC_MSG_RESULT([<=5.0 (< 500016)]) +# freebsd_pthread="4" +# fi +# fi +#fi + +if test "$adisc" = "default"; then + if test "$target" = "cygwin" ; then +# Don't do UDP source address discovery on Cygwin platform by default. + adisc="no" + else + adisc="yes" + fi +fi + +test "$adisc" = "yes" && AC_DEFINE(SRC_ADDR_DISC) + + +dnl Checks for libraries. +if test "$target" = "Linux"; then +AC_MSG_CHECKING([if we can compile and link with -pthread]) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -pthread" +AC_LINK_IFELSE([ +#include + +void *thread_func(void *data) +{ + *((int *)data)=1; + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int val; + + if(pthread_create(&thread, NULL, thread_func, &val)) + return 1; + + if(pthread_join(thread,&retval)) + return 1; + + return (*((int *)retval)!=1); +} +], + gcc_pthread_flag="yes", gcc_pthread_flag="no") +CFLAGS="$old_CFLAGS" +AC_MSG_RESULT([$gcc_pthread_flag]) + + if test "$gcc_pthread_flag" = yes ; then + thread_CFLAGS="-pthread" + AC_SUBST(thread_CFLAGS) + else + AC_CHECK_LIB(pthread, pthread_create) + fi +fi +if test "$target" = "BSD" -a `uname` != Darwin ; then +# if test $freebsd_pthread = 4 ; then + thread_CFLAGS="-pthread" + AC_SUBST(thread_CFLAGS) +# else +# AC_CHECK_LIB(c_r, pthread_create, , +# AC_MSG_ERROR([You must have libc_r installed to build/run pdnsd!])) +# fi; +fi + +if test "$target" = "Linux" -a "$threadlib" = default; then +AC_MSG_CHECKING([if this is an NPTL-based system]) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $thread_CFLAGS" +AC_RUN_IFELSE([ +#include +#include +#include +#include +#include +#include + +/* All this function does is return its PID (in a roundabout way). */ +void *thread_func(void *data) +{ + *((int *)data)=getpid(); + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int err,mainpid,thrdpid; + + err=pthread_create(&thread, NULL, thread_func, &thrdpid); + if(err) { + fprintf(stderr,"pthread_create failed: %s\n",strerror(err)); + return 1; + } + err=pthread_join(thread,&retval); + if(err) { + fprintf(stderr,"pthread_join failed: %s\n",strerror(err)); + return 1; + } + mainpid=getpid(); + /* In LinuxThreads implementations, the pids of the threads will usually differ + in a non Posix-compliant way. */ + fprintf(stderr,"main pid=%d, thread pid=%d\n",mainpid,*((int *)retval)); + return (*((int *)retval)!=mainpid); +} +], +[ + AC_MSG_RESULT([yes]) + threadlib=nptl +], +[ + AC_MSG_RESULT([no]) + threadlib=linuxthreads +], +[ + AC_MSG_RESULT([couldn't run test program]) + threadlib=linuxthreads +]) +CFLAGS="$old_CFLAGS" +fi + +if test "$threadlib" = nptl -o "$threadlib" = NPTL; then + AC_DEFINE(THREADLIB_NPTL) +elif test "$threadlib" = linuxthreads2 -o "$threadlib" = LinuxThreads2 -o "$threadlib" = lt2; then + AC_DEFINE(THREADLIB_LINUXTHREADS2) +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h sys/time.h syslog.h unistd.h) +AC_CHECK_HEADERS(sys/types.h sys/socket.h net/if.h netinet/in.h sys/poll.h,,, +[#include +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +]) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM +AC_CHECK_TYPES(socklen_t,, AC_DEFINE(socklen_t,int),[#include +#include +#include +#include ]) +AC_CHECK_TYPES([struct in6_addr, struct in_pktinfo, struct ifreq],,,[#include +#include +#include +#include ]) + +AC_MSG_CHECKING([for CPP C99 Variadic macro support]) +AC_COMPILE_IFELSE([ +#define a(...) junk(0,__VA_ARGS__) +extern void junk(int i,...); +int main() +{ + a(0); + a("a"); + a(0, "a", 1); + return 0; +} +], + cpp_c99_variadic="yes", cpp_c99_variadic="no") +AC_MSG_RESULT([$cpp_c99_variadic]) +if test $cpp_c99_variadic = yes; then + AC_DEFINE(CPP_C99_VARIADIC_MACROS) +else + if test "$GCC" != yes; then + AC_MSG_ERROR([Compiler must support C99 or gcc variadic macros]) + fi; +fi + +dnl Checks for library functions. +AC_FUNC_ALLOCA +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_SEARCH_LIBS(nanosleep, rt) +AC_SEARCH_LIBS(socket, socket,,,-lnsl) +AC_SEARCH_LIBS(inet_aton, resolv) +AC_CHECK_FUNCS(nanosleep gettimeofday mkfifo select socket strerror uname snprintf vsnprintf, true, + AC_MSG_ERROR([One of the functions required for pdnsd were not found.])) +AC_CHECK_FUNCS(poll, true, AC_DEFINE(NO_POLL)) +AC_CHECK_FUNCS(strdup strndup stpcpy stpncpy strlcpy mempcpy getline asprintf vasprintf getpwnam_r inet_ntop inet_pton) + +AC_OUTPUT([ +pdnsd.spec +Makefile +file-list.base +contrib/Makefile +doc/Makefile +doc/pdnsd.8 +doc/pdnsd.conf.5 +doc/pdnsd.conf +src/Makefile +src/pdnsd-ctl/Makefile +src/rc/Makefile +src/rc/RedHat/Makefile +src/rc/RedHat/pdnsd +src/rc/SuSE/Makefile +src/rc/SuSE/pdnsd +src/rc/Debian/Makefile +src/rc/Debian/pdnsd +src/rc/Slackware/Makefile +src/rc/Slackware/rc.pdnsd +src/rc/ArchLinux/Makefile +src/rc/ArchLinux/pdnsd +src/test/Makefile +PKGBUILD +]) diff --git a/app/src/main/jni/pdnsd/contrib/Makefile.am b/app/src/main/jni/pdnsd/contrib/Makefile.am new file mode 100644 index 0000000..5bdec20 --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/Makefile.am @@ -0,0 +1,2 @@ + +EXTRA_DIST = pdnsd_dhcp.pl dhcp2pdnsd change_pdnsd_server_ip.pl README diff --git a/app/src/main/jni/pdnsd/contrib/Makefile.in b/app/src/main/jni/pdnsd/contrib/Makefile.in new file mode 100644 index 0000000..a400dfe --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/Makefile.in @@ -0,0 +1,323 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = contrib +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = pdnsd_dhcp.pl dhcp2pdnsd change_pdnsd_server_ip.pl README +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu contrib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/contrib/README b/app/src/main/jni/pdnsd/contrib/README new file mode 100644 index 0000000..19e4f44 --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/README @@ -0,0 +1,30 @@ +Last revised: 27 July 2003 by Paul Rombouts + +This directory contains user-contributed scripts for use with pdnsd. +So far there are: + +pdnsd_dhcp.pl, save_ram.pl + A perl script contributed by Marko Stolle (derived from a script by + Mike Stella) to watch a ISC DHCPD leases file and add local records for + the hosts listed there. This makes pdnsd useable in a DHCP setup. + Please look into the script for usage instructions (you will probably + also need to customize some settings there). + For details about save_ram.pl, please look into pdnsd_dhcp.pl + +dhcp2pdnsd + A rc script for pdnsd, also by Marko Stolle. You might need to change + it slightly to make it run with your distro. + +change_pdnsd_server_ip.pl + A perl script contributed by Paul Rombouts for automatically updating + the configuration file if the DNS server configuration has changed. + For instance, you could place the following line in the script + /sbin/ifup-local + + /usr/local/sbin/change_pdnsd_server_ip.pl isplabel "$DNS" /etc/pdnsd.conf + + where $DNS contains the IP addresses (in comma separated format) of the + DNS servers obtained by DHCP negotiation. The perl script only + overwrites /etc/pdnsd.conf if the DNS configuration has actually + changed, in which case the previous configuration file is saved as + /etc/pdnsd.conf.save diff --git a/app/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl b/app/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl new file mode 100644 index 0000000..1eafca0 --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl -w +# +# A Perl script to change the ip addresses of dns servers +# in the pdnsd configuration file. +# +# Written by Paul A. Rombouts +# +# This file Copyright 2002, 2004 Paul A. Rombouts +# It may be distributed under the GNU Public License, version 2, or +# any higher version. See section COPYING of the GNU Public license +# for conditions under which this file may be redistributed. +# + +use strict; + +unless(@ARGV) {die "Error: no label specified.\n"} +my $label=shift; +unless(@ARGV) {die "Error: no DNS addresses specified.\n"} +my $dns_str=shift; +my $pdnsd_conf='/etc/pdnsd.conf'; +if(@ARGV) { + $pdnsd_conf=shift; + if(@ARGV) {warn "Warning: spurious arguments ignored: @ARGV\n"} +} + +#unless($label =~ /^\".*\"$/) {$label="\"$label\""} +#unless($dns_str =~ /^\".*\"$/) {$dns_str =~ s/^[\s,]*/\"/; $dns_str =~ s/[\s,]*$/\"/} +#unless($dns_str =~ /\"\s*\,\s*\"/) {$dns_str =~ s/[\s,]+/","/g} + +my @lines=(); +my $found_section=0; +my $changed=0; +my $ip_patt = qr/^((?:[^#]*?(?:\{|;))*?)(\s*ip\s*=\s*)("?[\w.:]+"?(?:\s*,\s*"?[\w.:]+"?)*)\s*;/; + +open(CONFFILE,$pdnsd_conf) or die "Can't open $pdnsd_conf: $!\n"; + +while() { + if(/^\s*server\s*\{/) { + my $sect_beg=$#lines+1; + my $sect_end; + my $found_label=0; + LOOP: { + do { + push @lines,$_; + if(/^(?:.*(?:\{|;))?\s*label\s*=\s*"?\Q$label\E"?\s*;/) { + if($found_label++) { + warn "Server section with multiple labels found.\n"; + close(CONFFILE); + exit 2; + } + } + if(/\}\s*$/) { + $sect_end=$#lines; + last LOOP; + } + } while(); + } + unless(defined($sect_end)) { + warn "Server section without proper ending found.\n"; + close(CONFFILE); + exit 2; + } + if(!$found_label) {next} + if(!($found_section++)) { + my $found_ip=0; + for(my $i=$sect_beg; $i<=$sect_end;++$i) { + if($lines[$i] =~ $ip_patt) { + my $matched=''; my $rest; + do { + $rest=$'; + if(!($found_ip++)) { + if($3 eq $dns_str) { + $matched.=$&; + } + else { + $matched.="$1$2$dns_str;"; + $changed=1; + } + } + else { + $matched.=$1; + $changed=1; + } + } while($rest =~ $ip_patt); + $lines[$i] = $matched.$rest; + } + } + if(!$found_ip) { + unless($lines[$sect_end] =~ s/\}\s*$/ ip=$dns_str;\n$&/) { + warn "Can't add ip specification to server section labeled $label.\n"; + close(CONFFILE); + exit 2; + } + $changed=1; + } + } + else { + splice @lines,$sect_beg; + $changed=1; + } + } + else {push @lines,$_} +} + +close(CONFFILE) or die "Can't close $pdnsd_conf: $!\n"; + +if(!$found_section) { + warn "No server sections labeled $label found.\n"; + exit 2; +} +elsif(!$changed) { + exit 0; +} + +rename($pdnsd_conf,"$pdnsd_conf.save") or die "Can't rename $pdnsd_conf: $!\n"; + +unless((open(CONFFILE,">$pdnsd_conf") or (warn("Can't open $pdnsd_conf for writing: $!\n"),0)) and + (print CONFFILE (@lines) or (warn("Can't write to $pdnsd_conf: $!\n"),0)) and + (close(CONFFILE) or (warn("Can't close $pdnsd_conf after writing: $!\n"),0))) { + rename("$pdnsd_conf.save",$pdnsd_conf) or die "Can't rename $pdnsd_conf.save: $!\n"; + exit 3; +} + +exit 1; diff --git a/app/src/main/jni/pdnsd/contrib/dhcp2pdnsd b/app/src/main/jni/pdnsd/contrib/dhcp2pdnsd new file mode 100644 index 0000000..73b631c --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/dhcp2pdnsd @@ -0,0 +1,45 @@ +#! /bin/sh +# +# dhcp2pdnsd Start/Stop DHCP to DNS update script +# +# chkconfig: 345 96 99 +# description: DHCP to DNS update script +# processname: dhcp2pdnsd.pl +# +# $Id: dhcp2pdnsd,v 1.1 2001/03/25 20:01:34 tmm Exp $ + +where="/usr/local/bin/" +name="pdnsd_dhcp.pl" + +# Source function library. +. /etc/rc.d/init.d/functions + +# Get config. +. /etc/sysconfig/network + +# See how we were called. +case "$1" in + start) + $where$name > /dev/null 2> /dev/null & + action "Starting DHCP to DNS update script: " /bin/true + ;; + stop) + p=`ps h -C $name | awk '{print $1}'` + [ $p -gt 0 ] 2> /dev/null && kill $p && action "Stopping DHCP to DNS update script: " /bin/true + [ $p -gt 0 ] 2> /dev/null || action "Stopping DHCP to DNS update script: " /bin/false + ;; + status) + p=`ps h -C $name | awk '{print $1}'` + [ $p -gt 0 ] 2> /dev/null && echo 'running as '$p + [ $p -gt 0 ] 2> /dev/null || echo 'not running' + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: dhcp2pdnsd {start|stop|status|restart|reload}" + exit 1 +esac + +exit 0 diff --git a/app/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl b/app/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl new file mode 100644 index 0000000..9cf0c87 --- /dev/null +++ b/app/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl @@ -0,0 +1,246 @@ +#!/usr/bin/perl +# $Id: pdnsd_dhcp.pl,v 1.2 2001/03/25 20:01:34 tmm Exp $ +########################################################################## +# +# Filename: pdnsd_dhcp.pl +# Description: Dynamic DNS-DHCP update script for pdnsd +# Author: Mike Stella +# Modified by: Marko Stolle +# Created: November 19, 2001 +# Last Updated: February 28, 2001 +# Email: fwd2m@gmx.de +# +########################################################################### +# +# This code is Copyright (c) 1998-2001 by Mike Stella and Marko Stolle +# +# NO WARRANTY is given for this program. If it doesn't +# work on your system, sorry. If it eats your hard drive, +# again, sorry. It works fine on mine. Good luck! +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +########################################################################### +# +# This script reads a dhcpd.leases file and dynamically updates pdnsd with +# hostname and ip information. +# +# It assumes that your DHCP server recieves hostnames from the +# clients, and that your clients offer their hostnames to the server. +# Some versions of Linux DHCP clients don't do that. I use ISC's +# DHCPD, found at http://www.isc.org - though others may work just +# fine. +# +# This version of the script updates the pdnsd database. The status +# control socket of pdnsd has to be enabled (psnsd -d -s). +# +########################################################################### +# +# 02/20/2001 - first working version +# 02/21/2001 - security patches by Thomas Moestl +# 02/22/2001 - re-read dhcpd.leases if ttl has expireds since last update +# 02/24/2001 - try to get domainname if not specified +# 02/28/2001 - randomized temporary filename +# added possibility to save some RAM (read below) +# +########################################################################### + + +# You may save some memory if you use absolute values with sysopen +# in sub update_dns and don't use tmpnam().. +# Just switch the '#' in front of the 'until sysopen' in the sub +# update_dns, check the necessary modes on your system using save_ram.pl +# and add a '#' in front of the following three lines. +# Not using the tmpnam() function may open a security breach on systems +# with not absolute trustworthy local users (Risk: a user may write a +# script which creates files with the same names as this script and block +# it that way. Unlikely because the filenames are now even without tmpnam() +# randomized and an attacker has to create a very large number of files.) + +use Fcntl; +use strict; +use POSIX qw(tmpnam); + +$|=1; + +########################################################################### +### Globals - you can change these as needed + +# Domain name +# if not changed script will try to get it from the system +my $domain_name = "domain"; + +# DHCPD lease file +my $lease_file = "/var/lib/dhcp/dhcpd.leases"; + +# path to pdnsd-ctl +my $pdnsd_ctl = "/usr/local/sbin/pdnsd-ctl"; + +# owning name server for the newly added records +my $nameserver = "localhost."; + +# TTL (Time To Live) for the new records +my $ttl = "86400"; + +# number of seconds to check the lease file for updates +my $update_freq = 30; + +my $debug = 0; + +########################################################################### +### Don't mess with anything below unless you REALLY need to modify the +### code. And if you do, please let me know, I'm always interested in +### in improving this program. + +# Make a pid file +`echo $$ > /var/run/pdnsd_update.pid`; + +my $logstr; +my $modtime = 0; +my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMP} || $ENV{TEMP}; + +use vars qw (%db); + +my $version = "1.03"; + + +########################################################################### +# Main Loop + + # try to find domainname if necessary + if ($domain_name eq "domain") { + $domain_name = `dnsdomainname`; + } + else { + $domain_name = "$domain_name\n"; + } + +while (1) { + + # check the file's last updated time, if it's been changed, update + # the DNS and save the time. Update DNS even if there a no changes on + # the leases file if ttl since last DNS update has expired. + # This will ALWAYS run once - on startup, since $modtime starts at zero. + + + my @stats = stat ($lease_file); + + + if (($stats[9] > $modtime) or (time >= $modtime+$ttl)){ + + # clear the old hash + undef %db; + + printf STDERR "updating DNS with dhcpd.leases\n"; + $modtime = time; + &read_lease_file; + &update_dns; + } + + # wait till next check time + sleep $update_freq; + +} # end main +########################################################################### + + +### write out the import file +sub update_dns { + my ($ip, $hostname, $fname); + + do { $fname = tmpnam() } + until sysopen(DNSFILE, $fname, O_WRONLY|O_CREAT|O_EXCL, 0600); +# do { $fname = "$temp_dir/d2d".int(rand(time())) } +# until sysopen(DNSFILE, $fname, 1|64|128, 0600); + + while (($hostname,$ip) = each (%db)) { + print DNSFILE "$ip $hostname.$domain_name"; + } + close DNSFILE; + + system ("$pdnsd_ctl source $fname $nameserver $ttl"); + unlink($fname); +} + + +### reads the lease file & makes a hash of what's in there. +sub read_lease_file { + + unless (open(LEASEFILE,$lease_file)) { + #`logger -t dns_update.pl error opening dhcpd lease file`; + print STDERR "Can't open lease file\n"; + return; + } + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $curdate = sprintf "%02d%02d%02d%02d%02d%20d%20d", + ($year+1900),($mon+1),$mday,$hour,$min,$sec; + + ## Loop here, reading from LEASEFILE + while () { + my ($ip, $hostname, $mac, $enddate,$endtime); + + if (/^\s*lease/i) { + + # find ip address + $_ =~ /^\s*lease\s+(\S+)/; + $ip = $1; + + # do the rest of the block - we're interested in hostname, + # mac address, and the lease time + while ($_ !~ /^}/) { + $_ = ; + # find hostname + if ($_ =~ /^\s*client/i) { + #chomp $_; + #chop $_; + $_ =~ /\"(.*)\"/; + $hostname = $1; + + # change spaces to dash, remove dots - microsoft + # really needs to not do this crap + $hostname =~ s/\s+/-/g; + $hostname =~ s/\.//g; + } + # get the lease end date + elsif ($_ =~ /^\s*ends/i) { + $_ =~ m/^\s*ends\s+\d\s+([^;]+);/; + $enddate = $1; + $enddate =~ s|[/: ]||g; + } + } + # lowercase it - stupid dhcp clients + $hostname =~ tr/[A-Z]/[a-z]/; + + ($debug < 1 ) || print STDERR "$hostname $ip $enddate $curdate\n"; + + # Store hostname/ip in hash - this way we can do easy dupe checking + if (($hostname ne "") and ($enddate > $curdate)) { + $db{$hostname} = $ip; + } + } + } + close LEASEFILE; +} + +### left around for testing +sub print_db { + my ($key,$value); + + while (($key,$value) = each (%db)) { + print "$key - $value\n"; + } +} + diff --git a/app/src/main/jni/pdnsd/depcomp b/app/src/main/jni/pdnsd/depcomp new file mode 100644 index 0000000..04701da --- /dev/null +++ b/app/src/main/jni/pdnsd/depcomp @@ -0,0 +1,530 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2005-07-09.11 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mecanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/app/src/main/jni/pdnsd/doc/Makefile.am b/app/src/main/jni/pdnsd/doc/Makefile.am new file mode 100644 index 0000000..b3e2198 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/Makefile.am @@ -0,0 +1,28 @@ + +man_MANS = pdnsd.8 pdnsd-ctl.8 pdnsd.conf.5 + +# Note: pdnsd-ctl.8, pdnsd.conf.5.in, dl.html and the txt docs are handled by dist-hook rule. +EXTRA_DIST = pdnsd.conf.in pdnsd.8.in \ + html/dl.html.in html/doc.html html/faq.html html/index.html \ + doc_makefile html/doc_makefile txt/doc_makefile \ + html2confman.pl html/htmlsubst.pl + +# XXX: Do not insist to set the config file owner to root to avoid breaking RPM +# builds +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" + if test `whoami` = "root"; then \ + $(INSTALL) -o 0 -g 0 -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + else \ + $(INSTALL) -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + fi + +dist-hook: doc_makefile + @$(MAKE) -f doc_makefile doc + cp -p --parents pdnsd-ctl.8 pdnsd.conf.5.in \ + html/dl.html txt/faq.txt txt/intro.txt txt/manual.txt \ + $(distdir) + +distclean-local: doc_makefile + @$(MAKE) -f doc_makefile clean + diff --git a/app/src/main/jni/pdnsd/doc/Makefile.in b/app/src/main/jni/pdnsd/doc/Makefile.in new file mode 100644 index 0000000..7f07fe6 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/Makefile.in @@ -0,0 +1,483 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = doc +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.8.in $(srcdir)/pdnsd.conf.5.in \ + $(srcdir)/pdnsd.conf.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd.8 pdnsd.conf.5 pdnsd.conf +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +man5dir = $(mandir)/man5 +am__installdirs = "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +man_MANS = pdnsd.8 pdnsd-ctl.8 pdnsd.conf.5 + +# Note: pdnsd-ctl.8, pdnsd.conf.5.in, dl.html and the txt docs are handled by dist-hook rule. +EXTRA_DIST = pdnsd.conf.in pdnsd.8.in \ + html/dl.html.in html/doc.html html/faq.html html/index.html \ + doc_makefile html/doc_makefile txt/doc_makefile \ + html2confman.pl html/htmlsubst.pl + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd.8: $(top_builddir)/config.status $(srcdir)/pdnsd.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pdnsd.conf.5: $(top_builddir)/config.status $(srcdir)/pdnsd.conf.5.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pdnsd.conf: $(top_builddir)/config.status $(srcdir)/pdnsd.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-man5: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)" + @list=''; test -n "$(man5dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.5[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ + done; } + +uninstall-man5: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man5dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.5[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man5dir)" && rm -f $$files; } +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)" + @list=''; test -n "$(man8dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man8dir)" && rm -f $$files; } +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @list='$(MANS)'; if test -n "$$list"; then \ + list=`for p in $$list; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ + if test -n "$$list" && \ + grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ + echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ + grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ + echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ + echo " typically \`make maintainer-clean' will remove them" >&2; \ + exit 1; \ + else :; fi; \ + else :; fi + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(MANS) +installdirs: + for dir in "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man5 install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-man + +uninstall-man: uninstall-man5 uninstall-man8 + +.MAKE: install-am install-data-am install-strip + +.PHONY: all all-am check check-am clean clean-generic dist-hook \ + distclean distclean-generic distclean-local distdir dvi dvi-am \ + html html-am info info-am install install-am install-data \ + install-data-am install-data-hook install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-man5 \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am uninstall-man uninstall-man5 uninstall-man8 + + +# XXX: Do not insist to set the config file owner to root to avoid breaking RPM +# builds +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" + if test `whoami` = "root"; then \ + $(INSTALL) -o 0 -g 0 -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + else \ + $(INSTALL) -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + fi + +dist-hook: doc_makefile + @$(MAKE) -f doc_makefile doc + cp -p --parents pdnsd-ctl.8 pdnsd.conf.5.in \ + html/dl.html txt/faq.txt txt/intro.txt txt/manual.txt \ + $(distdir) + +distclean-local: doc_makefile + @$(MAKE) -f doc_makefile clean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/doc/doc_makefile b/app/src/main/jni/pdnsd/doc/doc_makefile new file mode 100644 index 0000000..f5e8f86 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/doc_makefile @@ -0,0 +1,38 @@ +# This file was written by Paul Rombouts. +# Because pdnsd currently has a very idiosyncratic method of building documentation +# I prefer to keep the actual build rules outside of the Makefiles an ordinary user +# would use to compile pdnsd and therefore I have put them into separate 'doc_makefile's. +# +# To rebuild pdnsd docs after you have modified something that other files depend on, +# run 'make -f doc_makefile doc' in the doc/ directory. +# This makefile is also invoked when you build a pdnsd distribution tarball +# using 'make dist' in the toplevel pdnsd source directory. +# +# If anyone thinks there is a much more elegant method for building the pdnsd docs +# using a conventional autoconf/automake process, please let me know. + +versionfile = ../version + +doc: pdnsd-ctl.8 pdnsd.conf.5.in html txt +.PHONY: pdnsd-ctl.8 doc html txt clean + +pdnsd-ctl.8: + @pver=`cat $(versionfile)` && \ + mver=`perl -e 'while(<>) {if(/^\s*\.TH(?:\s+(?:"[^"]*"|[^"\s]+)){3}\s+"pdnsd\s+([^"]*)"/) {print "$$1\n";exit 0}} \ + die "Cannot find version in $$ARGV\n"' $@` && { \ + test "$$mver" = "$$pver" || { \ + perl -p -i.makesave -e 's/^(\s*\.TH(?:\s+(?:"[^"]*"|[^"\s]+)){3}\s+"pdnsd\s+)[^"]*(")/$${1}'"$$pver"'$${2}/' $@ && \ + echo "Updated version in $@: $$mver -> $$pver"; \ + }; \ + } + +pdnsd.conf.5.in: html/doc.html html2confman.pl + perl html2confman.pl $< > $@ + +html txt: + @cd $@ && $(MAKE) -f doc_makefile + +clean: + @rm -fv pdnsd.conf.5.in + @cd html && $(MAKE) -f doc_makefile clean + @cd txt && $(MAKE) -f doc_makefile clean diff --git a/app/src/main/jni/pdnsd/doc/html/dl.html b/app/src/main/jni/pdnsd/doc/html/dl.html new file mode 100644 index 0000000..3f9c614 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/dl.html @@ -0,0 +1,96 @@ + + + + pdnsd Download Page + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Download Page

+ +

The original author of pdnsd is Thomas Moestl, + but since 2003, he no longer maintains pdnsd. + However, Paul A. Rombouts has extensively revised the code and maintains + a version with many fixes and improvements at + http://members.home.nl/p.a.rombouts/pdnsd.html.
+ He has pre-patched tarballs and RPM packages available for download at this site.
+

+

+ If you are interested in the very latest code or if you want to participate in + pdnsd development, checkout the pdnsd git repository + at gitorious.org. +

+ + The most recent tarball is pdnsd-1.2.9b-par.tar.gz + (GPG signature).
+ The most recent RPM packages are: + + + + + + + + + + + + + + + + + + + + + +
Package NameSizeDescription
pdnsd-1.2.9b-par.src.rpm514kBSource RPM (binary packages can also be built directly from the tarball). +
pdnsd-1.2.9b-par_sl6.i686.rpm???i686 binary built on a Scientific Linux 6.2 system. +
pdnsd-1.2.9b-par_sl6.x86_64.rpm282kBx86_64 binary built on a Scientific Linux 6.2 system. +
+ If you want to check the signatures on these packages you will need a copy of my GPG key + which you can get here + or from a public key server. +

+ There are also Debian, + Ubuntu, + Gentoo, + Mandriva + and FreeBSD + packages, but these may not include the latest version.
+ If you are looking for other versions released by Paul Rombouts, visit this + download directory. +

+
+
+

+ Last revised: 24 Apr 2012 by Paul A. Rombouts +

+ + diff --git a/app/src/main/jni/pdnsd/doc/html/dl.html.in b/app/src/main/jni/pdnsd/doc/html/dl.html.in new file mode 100644 index 0000000..dffe2b0 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/dl.html.in @@ -0,0 +1,96 @@ + + + + pdnsd Download Page + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Download Page

+ +

The original author of pdnsd is Thomas Moestl, + but since 2003, he no longer maintains pdnsd. + However, Paul A. Rombouts has extensively revised the code and maintains + a version with many fixes and improvements at + http://members.home.nl/p.a.rombouts/pdnsd.html.
+ He has pre-patched tarballs and RPM packages available for download at this site.
+

+

+ If you are interested in the very latest code or if you want to participate in + pdnsd development, checkout the pdnsd git repository + at gitorious.org. +

+ + The most recent tarball is pdnsd-$version.tar.gz + (GPG signature).
+ The most recent RPM packages are: + + + + + + + + + + + + + + + + + + + + + +
Package NameSizeDescription
pdnsd-$version.src.rpm$sizeof("$HOME/rpmbuild/SRPMS/pdnsd-$version.src.rpm")Source RPM (binary packages can also be built directly from the tarball). +
pdnsd-${version}${extver}.${arch}.rpm$sizeof("$HOME/rpmbuild/RPMS/pdnsd-${version}${extver}.${arch}.rpm")${arch} binary built on a $system. +
pdnsd-${version}${extver}.${arch2}.rpm$sizeof("$HOME/rpmbuild/RPMS/pdnsd-${version}${extver}.${arch2}.rpm")${arch2} binary built on a $system. +
+ If you want to check the signatures on these packages you will need a copy of my GPG key + which you can get here + or from a public key server. +

+ There are also Debian, + Ubuntu, + Gentoo, + Mandriva + and FreeBSD + packages, but these may not include the latest version.
+ If you are looking for other versions released by Paul Rombouts, visit this + download directory. +

+
+
+

+ Last revised: $date by Paul A. Rombouts +

+ + diff --git a/app/src/main/jni/pdnsd/doc/html/doc.html b/app/src/main/jni/pdnsd/doc/html/doc.html new file mode 100644 index 0000000..95828c5 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/doc.html @@ -0,0 +1,2630 @@ + + + + pdnsd Documentation + + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Documentation

+ This is the "official" pdnsd documentation and reference written by + Thomas Moestl with revisions by + Paul A. Rombouts.
+ This manual is a part of the pdnsd package, and may be distributed in + original or modified form under terms of the GNU General Public + License, as published by the Free Software Foundation; either version + 3, or (at your option) any later version.
+ You can find a copy of the GNU GPL in the file COPYING in the source or documentation directory.
+ This manual is up-to-date for version 1.2.9b. For older documentation, please refer to the doc + directory of the respective pdnsd package.
+ If you want a quicker introduction to pdnsd, you can try some of the + HOWTOs available on the web. + For Apple Mac users, Brian Wells has published a good HOWTO at + http://web.mac.com/brianwells/main/pdnsd.html. + + +

0. Installation

+

0.1 Installing binary RPM's

+ To install a binary RPM, just do
+

rpm -i pdnsd-<version>.rpm

+ This should install pretty much everything automatically. The only thing left + for you to do is adapt your configuration file (stored in /etc/pdnsd.conf) + according to your needs (see below). + In the Red Hat and SuSE RPMs, a start script is also installed; read the section + 0.4, Start at Boot Time about that. + +
+

0.2 Building RPM's

+ It is possible to build a binary RPM from a source package using the command
+

rpmbuild --rebuild pdnsd-<version>.src.rpm

+ or alternatively from a tarball using the command
+

rpmbuild -tb pdnsd-<version>.tar.gz

+ You can do this as root, but it is safer to build a binary package first as a normal user, + and then, when all has gone well, install the resulting binary package as root as in the previous section. + How to build an RPM package without being root is described at + + http://www.ibm.com/developerworks/linux/library/l-rpm1/.

+ Several pdnsd-specific options are available when building RPM packages: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ --with isdn + + Has the same effect as --enable-isdn (see below). +
+ --without poll + + Has the same effect as --disable-poll (see below). +
+ --without nptl + + Has the same effect as --with-thread-lib=linuxthreads (see below). +
+ --with ipv6 + + Has the same effect as --enable-ipv6 (see below). +
+ --without tcpqueries + + Has the same effect as --disable-tcp-queries (see below). +
+ --without debug + + Has the same effect as --with-debug=0 (see below). +
+ --define "distro <distro>" + + Has the same effect as --with-distribution=<distro> (see below). +
+ --define "run_as_user <user>" + + Has the same effect as --with-default-id=<user> (see below).
+ For RPMs the default <user> is "pdnsd". +
+ --define "run_as_uid <uid>" + + If the user defined by the previous option does not exist when the RPM is installed, + the pre-install script will try to create a new user with numerical id <uid>. + The default is to let the system choose the numerical id at install time. +
+ --define "cachedir <dir>" + + Has the same effect as --with-cachedir=<dir> (see below). +
+ You can also configure which compiler flags will be used by setting the environment variable + CFLAGS. + Using a bash shell, you can do that on the command line like this: +  CFLAGS="-O1 -Wall" rpmbuild ...
+ This is useful if you prefer a different level of optimization, for instance. + +
+

0.3 Installing from pure sources (tar archives or git repositories)

+

0.3.1 Setting up the source code tree

+ Source code is available in the form of snapshots (tarballs) or a git repository + with the very latest development code and a (nearly) complete history of all the revisions. + Cloning a git repository is useful if you need a recent fix or feature + that is not yet contained in a main release or you want to participate in pdnsd development. + Otherwise you will probably find the tarballs more convenient because they are much more compact. +
0.3.1.1 Unpacking a tar archive
+ The pdsnsd snapshot releases come in the form of a gzip'ed tar archive. + To decompress it (using a modern tar) do
+

tar -xzf pdnsd-<version>.tar.gz

+ If your tar doesn't do this, use:
+

gzip -dc pdnsd-<version>.tar.gz | tar -xf -

+
0.3.1.2 Cloning a git repository
+ To clone a git repository you need to install, if not already installed, + the git version control system, which is available as a package in most modern Linux distributions. + Then run the command:
+

git clone git://gitorious.org/pdnsd/pdnsd.git pdnsd

+ In rare cases, if you are behind some kind of firewall, the special git protocol can't be used + and you will need to fall back to the http protocol. + See the gitorious.org website or git documentation for more information. + +

0.3.2 Configuring the source

+ Change into the pdnsd source directory and run configure. It takes the following command line + options (if you do not specify an option, defaults will be used):
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ --prefix=dir + + Specify the prefix directory. The pdnsd files are installed in subdirectories + of the prefix, the pdnsd and pdnsd-ctl executables are for example installed + in the sbin subdirectory of the prefix. The default for this is /usr/local; + you might want to set this to /usr (using --prefix=/usr). +
+ --sysconfdir=dir + + Specify the config directory. pdnsd expects its pdnsd.conf file to reside + there if the -c option is not given at startup. + The default for this is the etc subdirectory of your prefix, e.g. /usr/local/etc + if you did not specify a prefix. To set this e.g. to /etc, use --sysconfdir=/etc. +
+ --with-distribution=distro + + Specify target distribution (default=Generic; others: RedHat, SuSE, Debian)
+ See below for the effect of these settings. +
+ --with-target=platform + + Change compilation target platform (default: autodetect; others: Linux, BSD, Cygwin).
+ autodetect will attempt to detect whether you are using Linux, *BSD or Cygwin and + should normally be sufficient. If this does not work, try specifying + your system manually (for the Darwin platform (Apple Mac OS X) specify BSD here). +
+ --with-cachedir=dir + + Default directory for pdnsd cache (default=/var/cache/pdnsd)
+ This setting can be changed via config file settings when pdnsd has been built. +
+ --with-hash-buckets=num + + Number of hash buckets to use (default=1024). The default should be + sufficient for most purposes, but if you want to store a large number of names + in the cache, cache lookups may be faster if the number of hash buckets + is comparable to the number of names stored in the cache. + The number actually used is the smallest power of two + greater or equal to the number specified here. +
+ --enable-isdn + + Enable ISDN support
+ This option will work only on Linux and may cause problems with 2.0.x or + old 2.2.x kernels. You will need it for a proper if uptest + under Linux for ISDN ppp devices. +
+ --disable-ipv4 + + Disable IPv4 networking support (default=enabled) +
+ --enable-ipv6 + + Enable IPv6 networking support.
+ If your OS does support IPv6 properly, you should be able to serve also + IPv4 queries using this. Normally, this is disabled and you won't need + it. +
+ --disable-ipv4-startup + + Disable IPv4 on pdnsd startup by default (default=enabled) +
+ --enable-ipv6-startup + + Enable IPV6 on pdnsd startup by default (default=IPv4). + These options are only defaults, you can specify on + the command line or in the config files which IP version + will really be used. + Normally, you won't need to change these. +
+ --disable-udp-queries + + Disable UDP as query method. You shouldn't need to change + this. +
+ --disable-tcp-queries + + Disable TCP as query method. This only effects the querying of + name servers by pdnsd, not the ability of pdnsd to answer + TCP queries from clients. + TCP queries are slower than UDP queries, but can be more secure + against certain types of attacks and are able to handle large answers. + For normal use this can be disabled. + (Note that the default has changed: TCP-query support + is now compiled in by default, but it still depends on the run-time + options whether it is actually used.) +
+ --with-query-method=qm + + Specify the query method (default=udponly, others: tcponly, tcpudp, udptcp). + If you have enabled both UDP and TCP queries, this lets you control + which query method pdnsd will use by default. tcpudp will try TCP + first and fall back to UDP if TCP is not supported by the server; + udptcp will try UDP first and, if the answer was truncated, will repeat + the query using TCP. + udponly and tcponly should be clear. Note that this only effects + the compiled-in default; the query method can still be changed using + command-line options or options in the configuration file. +
+ --disable-tcp-server + + Disable the TCP server. + In this case pdnsd will not be able to respond to TCP queries from clients. + This may cause problems with very large answers. +
+ --disable-src-addr-disc + + Disable the UDP source address discovery.
+ You need this only if you have trouble with messages saying + "could not discover udp source address".
+ For the Cygwin target, this option is disabled by default. +
+ --disable-poll + + Disable poll(2) and use select(2) (default=enabled)
+ You will normally not need this. +
+ --disable-new-rrs + + Since version 1.2.9 this option is obsolete and ignored. + It is now possible to configure for each RR type separately whether it is + cacheable by pdnsd by editing the file src/rr_types.in. + The comments in this file explain how to do this. +
+ --enable-strict-rfc2181 + + Enforce strict RFC 2181 compliance.
+ This will cause pdnsd to reject DNS answers with incorrect + timestamp settings (multiple RRs of the same type and for the same domain with + different TTLs). Normally not needed. +
+ --enable-underscores + + This option is obsolete. Since version 1.2, pdnsd places no restrictions + on the types of characters in domain names (there are still a few restrictions + for locally defined names, though). +
+ --with-random-device=device + + Specify random device; default: C Library random() PRNG
+ pdnsd uses (pseudo-) random numbers as query IDs for security reasons + (this makes forging DNS answers more difficult). This option + controls where pdnsd gets these from. The default is the C library + random() function, which is relatively weak. + You can specify a device like /dev/urandom here if you like; pdnsd will read + random numbers from it 16-bit-wise. /dev/urandom is present under Linux and + most BSD derivates. You should not use /dev/random - it is more secure, but + may block and delay pdnsd's answers for a long time.
+ You can specify arc4random to use the BSD arc4random() + library function (default for FreeBSD target), which is considered safe.
+ You can also specify random as device to use the C Library + random() function (described above). +
+ --with-default-id=user + + Specify default user for pdnsd (default=nobody). + This is the user that will be entered for the run_as + option in the config file (see below) that will be installed during make install. + You can change this any time in your config file. +
+ --with-debug=level + + Specify debugging level. Normally you can safely switch debugging off + by setting the level to 0. This will increase speed (although only + marginally) and save space in the executable (only about 12kB). + However, more significant may be the savings in stack space, especially + if pdnsd is put under heavy load and there are many simultaneous + running threads.
+ Presently the only defined debug levels are in the range 0 - 9. + Setting the level to 9 enables hex dumps of the queries and replies + pdnsd receives and should normally not be needed. Debug output will only + be generated if you turn on special switches; it might be useful for + debugging your config files, so I recommend using the default (1). + However, if you use pdnsd under heavy load, a better strategy may be + to compile one version of pdnsd without debug support (configured with + --with-debug=0) for production use, and one version with + with debug support (e.g. --with-debug=9) + for diagnostic purposes. +
+ --with-verbosity=level + + Specify default message verbosity. The default should be ok. +
+ --enable-rcsids + + Enable RCS IDs in executables (default=disabled).
+ For personal use, there is no need to do this. If you build rpm's, it + might have advantages. +
+ --enable-tcp-subseq + + Enable subsequent tcp queries. The DNS protocol standard + requires that servers must be capable of answering multiple + subsequent queries that are sent over the same tcp connection, and that + the server may only close the connection by himself after a certain + timeout. This feature is rarely used, but may make denial-of-service + attacks easier, as it allows for an attacker to hold a connection open + a long time (although the attacker's IP is most likely revealed then). + For full standard compliance, you should use this option. + If you do not use --enable-tcp-server, is option is not honored. +
+ --with-tcp-qtimeout=secs + + Specify default tcp query timeout after which the connection is closed + if no full query has been received. The default is 30s. + You can also change this option at run time using the tcp_qtimeout + config file option. + If you do not use --enable-tcp-server, is option is not honored. +
+ --with-par-queries=num + + Specify the default number of queries that can be executed in parallel. + You can also change this option at run time using the par_queries + config file option. See the description of that option for an explanation + of what it really does.
+ The default for this option is 2. +
+ --with-max-nameserver-ips=num + + New in version 1.2.9b: + Specify the maximum number of IP addresses that can be used per nameserver obtained + from NS records (when resolving names recursively). + Just one IP address per nameserver is sufficient in the vast majority of cases + (and this was the strategy used by pdnsd in previous versions), + but in rare cases this will cause unnecessary resolve failures if the address chosen + for each nameserver happens to be unreachable while the other addresses would lead to + successful resolution.
+ The default for this option is 3. +
+ --with-thread-lib=lib + + Added by Paul Rombouts: Use this option if you experience problems with + signal handling under Linux. The usual symptom is that pdnsd fails to save + the cache to disk, and /var/cache/pdnsd/pdnsd.cache remains empty. + If you experience this kind of trouble, try reconfiguring with different values + for the --with-thread-lib option. The allowable values are + linuxthreads (or lt for short), linuxthreads2 + (or lt2 for short), and nptl. + By default the configure script tries to detect automatically whether + linuxthreads or nptl is more appropriate for your system, + but the method used is not foolproof. Look for the line: + checking if this is an NPTL-based system...
+ If the automatic test mistakenly indentifies the thread library on your system as + NPTL based, you should reconfigure with --with-thread-lib=lt and recompile. + If the result of the automatic test is "no" or if + --with-thread-lib=lt does not have the desired effect, try again using + --with-thread-lib=lt2 . +
+ Normally, you will need only --prefix, --sysconfdir and + --with-distribution. + If you specify your distribution using --with-distribution, this has the + following effects: +
    +
  • An rc script is copied in the appropriate localtion, which enables pdnsd to start + at machine boot time (see 0.4) +
  • Distribution-specific portions might be included in the generated pdnsd.spec + file (only important if you want to build rpm archives yourself). +
+ If you choose Generic, no rc script is installed, and a generic spec + file is generated.
+ + Further instructions are in the INSTALL document in the pdnsd source directory. + ./configure --help will give you a list of all supported command line + options.

+ Note added by Paul Rombouts: Some people may want change the compiler optimization flag. + I use the -O2 flag, but it might be safer to use a lower level of + optimization or no optimization at all. In that case prefix the + configure command with the desired compiler flags like this + (assuming you're using a bash shell): +

CFLAGS="-O1 -Wall" ./configure ...

+ + +
+

0.3.3 Building & installing

+ Type make in the source directory. Should work by now.
+ To install, type make install or do the installation by hand (see 0.3.4).
+ make install will do the following ($prefix is the prefix directory; see above):
+
    +
  1. copies pdnsd to $(prefix)/sbin/ +
  2. copies pdnsd-ctl to $(prefix)/sbin/ +
  3. copies docs/pdnsd.conf.sample (a sample configuration) to the pdnsd config directory. +
  4. creates your cache directory if it is not there. + After installation, you should check the file permissions and create or edit + /etc/pdnsd.conf to fit your needs (see below). + If you use the run_as option, please make sure that your + cache directory is owned by the user you specified with this option! +
+ You must be root for this installation!
+ Security notes: never make the pdnsd cache directory + writeable for untrusted users, or you will get several security holes: + the users might modify the cache contents, or plant dangerous links.
+ If you use a pidfile, you should be aware that you introduce security + problems if you place the pidfile in a directory in a NFS filesystem that + is writeable for untrusted users. Generally, the pidfile directory + (typically /var/run) should not be writeable for untrusted users. +
+

0.3.4 Manual installation

+ For a manual installation, you need to do the following steps: +
    +
  1. Copy pdnsd and pdnsd-ctl from your build directory to an appropriate location (e.g. /usr/sbin). +
  2. Copy docs/pdnsd.conf into the directory you want it to reside (/etc by default, + and change it according to your needs (see below). +
  3. Create your caching directory; default is /var/cache/pdnsd (you may change this + in your pdnsd.conf); Permissions should be at max rwxr-xr-x (if you want to + protect your cache and status socket, make it rwx------). +
+ Thats it! +
+ +

0.4 Start at boot time

+ In the src/rc folder of the pdnsd distribution are start scripts + for pdnsd designed for different Linux distros. There are scripts + for SuSE, Redhat, Debian, Arch Linux and Slackware now.
+ The start scripts are automatically installed during RPM install, and also during make install + if you specified your distro.
+ For Slackware Linux there is a start-up script contributed by Nikola Kotur, but presently + it must be installed manually. + See src/rc/README and src/rc/Slackware/rc.pdnsd for details. + +

0.4.1 SuSE Linux startup

+ rc/SuSE/pdnsd is a start script for SuSE Linux. It was tested for 6.? but should run on some + versions below. You can do make install as root in the rc/SuSE + directory to install it, or you can install manually:
+ + + + + + + +
+ manual installation +
+ For manual installation, copy rc/SuSE/pdnsd into /sbin/init.d/, go to + /sbin/init.d/rc2.d/ and create there the following two symlinks:
+ S11pdnsd to ../pdnsd (do ln -s ../pdnsd S11pdnsd in that dir)
+ K34pdnsd to ../pdnsd (do ln -s ../pdnsd K34pdnsd in that dir)
+ The numbers dictate the order different services are started and + might need to be modified. Then edit your /etc/rc.config file and + add the line START_PDNSD=yes to start pdnsd at boot time. +
+

+ If you used the make install command, START_PDNSD=yes has been + appended to your /etc/rc.config file, causing pdnsd to be started + at boot time. If you don't want that, change the yes into no. +

+ This start script was created from /sbin/init.d/skeleton by me, so the + most is copyrighted by SuSE. They put it under the GPL, however, so + the license stated in COPYING also applies to this script. + There is NO WARRANTY OF ANY KIND on these scripts. + This is no official SuSE script, and SuSE naturally does NO support + for it. +

0.4.2 Red Hat Linux startup

+ rc/Redhat/pdnsd is a start script for Red Hat Linux. It was contibuted by Torben + Janssen.
+ This was tested for 6.1 but should run on 5.0+. You can do make install as root in the + rc/Redhat directory to install it, or you can install manually:
+ + + + + + + +
+ manual installation +
+ For manual installation, copy rc/Redhat/pdnsd into /etc/rc.d/init.d/
+ Then go to /etc/rc.d/rc3.d and create there the following symlink:
+ S78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd S78pdnsd in that dir)
+ + Then go to /etc/rc.d/rc0.d and create there the following symlink:
+ K78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir)
+ + Then go to /etc/rc.d/rc6.d and create there the following symlink:
+ K78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) +
+ This script is also covered by license stated in COPYING. + Again, there is NO WARRANTY OF ANY KIND on these scripts. + This is no offical Redhat script, and Redhat naturally does NO support + for it +
+

0.5 Notes for FreeBSD users

+ The special handling of ISDN ppp devices is only supported on Linux. It is not needed in FreeBSD, the normal + device handling also works fine with isdn4bsd devices.
+ When compiled for FreeBSD, pdnsd as a small RFC compatability issue: RFC2181 demands answers on dns querys + to be sent with the same source address the query packet went to. In seldom cases, this will not be the case, + because the kernel selects the source address depending on the interface that was used for sending the answer.
+ Setting the source address currently does not work for IPv4. I have written a kernel patch that will provide an easy way + to program this. We'll see if or when it gets commited.
+
+
+

1 Invocation

+ When invoking pdnsd, you can specify various options at the command line. Command line options + always override config file options. The various --noX options are present to override + config file options. +

+ pdnsd --help (or -h) gives you an overview of the pdnsd command line options. +

+

+ pdnsd --version (or -V for short) prints licence and version information. +

+

+ To start pdnsd as background daemon, specifiy --daemon (or -d for short) on + the command line. Diagnostic and error messages after the actual daemon start + will be printed to the syslog instead of the console. --nodaemon will disable this. +

+

+ When starting pdnsd as a daemon, the -p option may be helpful: It writes the pid + of the server process to the file of the name given as argument to this option.
+ Example: pdnsd -d -p /var/run/pdnsd.pid +

+

+ If you want to specify a configuration file other than /etc/pdnsd.conf, specify + -c or --config-file on the command line, followed by a filename. +

+

+ If pdnsd was compiled with debugging support, you may specify -g or + --debug on the command line. This will cause extra diagnostic messages to be + printed. When pdnsd runs in daemon mode, the messages will be written to the pdnsd.debug + file in your cache directory. --nodebug disables debugging. +

+

+ pdnsd -vn sets the verbosity level of pdnsd. n is normally a digit from 0 to 3, + where 0 means normal operation, while 3 will most verbose. + Level 9 can be used in combination with the --debug option for very + extensive debug information.
+ Note: The current implementation mostly ignores the verbosity level, + so you may not notice much difference between the various levels. +

+

+ The option -s or --status enables the status control socket. This is a named socket in + the cache directory called pdnsd.status. This socket allows run-time configuration of pdnsd + using the utility pdnsd-ctl. See below for more details about pdnsd-ctl. + --nostatus disables status control. + See also the configuration option status_ctl in the global section. +

+

+ The option --notcp disables the seldom needed TCP server thread, which may + save you some resources. -t or --tcp will enable it. + See also the tcp_server configuration option. +

+

+ Using the -m option, you can select the method pdnsd uses to query other name servers. + Following methods are supported (see also the query_method + configuration option):
+ -muo: pdnsd will use UDP only. This is the fastest method, and should be supported by all name servers + on the Internet.
+ -mto: pdnsd will use TCP only. TCP queries usually take longer time than UDP queries, but are more secure + against certain attacks, where an attacker tries to guess your query id and to send forged answers. TCP queries + are not supported by some name servers.
+ -mtu: pdnsd will try to use TCP, and will fall back to UDP if its connection is refused or times out.
+ -mut: New in version 1.2.5: pdnsd will try to use UDP, and will repeat the query using TCP if the UDP reply was truncated + (i.e. the tc bit is set). This is the behaviour recommended by the DNS standards.
+

+

+ The -4 option switches to IPv4 mode, providing pdnsd was compiled with IPv4 support.
+ The -6 option switches to IPv6 mode, providing pdnsd was compiled with IPv6 support.
+ The -a option is only available when pdnsd was compiled with both IPv4 and IPv6 support. + With this option, pdnsd will try to detect automatically if a system supports IPv6, and fall back to IPv4 otherwise.
+

+

+ With -i prefix or --ipv4_6_prefix=prefix you can set the prefix pdnsd uses (when running in IPv6 + mode) to map IPv4 addresses in the configuration file to IPv6 addresses. + There is also a corresponding option for the config file, see below. + Must be a valid IPv6 address. + The default is ::ffff:0.0.0.0 +

+ +

2 The configuration file

+ This section describes the layout of the configuration file and the available + configuration options. + The default location of the file is /etc/pdnsd.conf. This may be changed + with the -c command line option. + An example pdnsd.conf comes with the pdnsd distribution in the docs directory + and will be installed to /etc/ by make install. + +
+

2.1 Layout

+ The configuration file is divided into sections. Each section is prefixed with + the section name and opening curlies ({) and closed with closing curlies (}). + In each section, configuration options can be given in the form +
+ option_name=option_value; +
+ Option value may be a string literal, a number, a time specification or a constant. + In previous versions of pdnsd strings had to be enclosed + in quotes ("), but since version 1.1.10 this is no longer necessary, unless + a string contains a special character such as whitespace, a token that normally starts + a comment, or one of ",;{}\". + Since version 1.2.9 a backslash (\) inside a string is interpreted as an escape character, + so it is possible to include special characters in strings (both quoted or unquoted) + by preceding them with a backslash. Some escape sequences are in interpreted as in the C + programming language, e.g. \t becomes a tab, + \n becomes a new-line control char.
+ A time specification consists a sequence of digits followed by a one-letter suffix. + The following suffixes are recognized: + s (seconds), m (minutes), h (hours), + d (days) and w (weeks). + If the suffix is missing, seconds are assumed. + If several time specifications are concatenated, their values are added together; + e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds.
+ Some options take more than one value; in this case, the values are separated with commas.
+ If you may supply one of a set of possible values to an option, this is noted + in the documentation as + (option1|option2|option3|...)
+ The constants true|false and yes|no + are accepted as synonyms for the constants on|off.
+ Comments may be enclosed in /* and */, nested comments are possible. If the + # sign or two slashes (//) appear in the configuration file, everything from + these signs to the end of the current line is regarded as a comment and ignored.
+ There are examples for nearly all options in the sample config file. +
+

2.1.1 global Section

+ The global section specifies parameters that affect the overall behaviour of the + server. If you specify multiple global sections, the settings of those later in + the file will overwrite the earlier given values.
+ These are the possible options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ perm_cache=(number|off);
+ Switch the disk cache off or supply a maximum cache size in kB. If the disk + cache is switched off, 8 bytes will still be written to disk. + The memory cache is always 10kB larger than the file cache. + This value is 2048 (2 MB) by default. +
+ cache_dir=string;
+ Set the directory you want to keep the cache in. + The default is "/var/cache/pdnsd" + (unless pdnsd was compiled with a different default). +
+ server_port=number;
+ Set the server port. This is especially useful when you want to start the + server and are not root. Note that you may also not specify uptest=ping in + the server section as non-root.
+ The default port is 53, the RFC-standard one. Note that you should only use + non-standard ports when you only need clients on your machine to communicate + with the server; others will probably fail if the try to contact the server + on the basis of an NS record, since the A record that supplies the address for + (among others) name servers does not have a port number specification. +
+ server_ip=string;
+ or
+ interface=string;
+ Set the IP address pdnsd listens on for requests. This can be useful + when the host has several interfaces and you want pdnsd not to listen on + all interfaces. For example, it is possible to bind pdnsd to listen on + 127.0.0.2 to allow pdnsd to be a forwarder for BIND. + The default setting for this option is server_ip=any, which means that + pdnsd will listen on all of your local interfaces. + Presently you can only specify one address here; if you want pdnsd to listen on multiple + interfaces but not all you will have to specify server_ip=any + and use firewall rules to restrict access.
+ The IP address used to need quotation marks around it, but since version 1.1.10 + this is no longer necessary.
+ If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to + specify an IPv6 address here, then unless pdnsd was compiled to start up in IPv6 mode + by default, you will need to use the -6 command-line option or + set run_ipv4=off first (see below) in order to ensure that the + IPv6 address is parsed correctly.
+ If pdnsd is running in IPv6 mode and you specify an IPv4 address here, + it will automatically be mapped to an IPv6 address.
+ New in version 1.2: You may also give the name of an interface + such as "lo" or "eth0" here, instead of an IP address + (this has been tested on Linux, and may or may not work on other platforms). + pdnsd will not bind to the interface name, but will look up the address of the + interface at start-up and listen on that address. If the address of the interface + changes while pdnsd is running, pdnsd will not notice that. You will need to + restart pdnsd in that case. +
+ outgoing_ip=string;
+ or
+ outside_interface=string;
+ New in version 1.2.9: + Set the IP address of the interface used by pdnsd for outgoing queries. + This can be useful when the host has several interfaces and you want pdnsd + to send outgoing queries via only one of them. + For example, if pdnsd is running on a host with one interface with IP address + 192.168.1.1 connected to the local network, and another with IP address 123.xxx.yyy.zzz + connected to the internet, you may specify server_ip=192.168.1.1 + and outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds + to queries received from the local network, and only sends outgoing queries via + the interface connected to the internet.
+ The default setting for this option is any, which means that + the kernel is free to decide which interface to use. + Like with the server_ip option, you may also give the name of an + interface here, instead of an IP address. +
+ linkdown_kluge=(on|off);
+ This option enables a kluge that some people might need: when all servers are + marked down, with this option set the cache is not even used when a query is + received, and a DNS error is returned in any case. The only exception from this + is that local records (as specified in rr and source + sections are still served normally. + In general, you probably want to get cached entries even when the network is down, + so this defaults to off. +
+ max_ttl=timespec;
+ This option sets the maximum time a record is held in cache. All dns + resource records have a time to live field that says for what period of time the + record may be cached before it needs to be requeried. If this is more than the + value given with max_ttl, this time to live value is set to max_ttl. + This is done to prevent records from being cached an inappropriate long period of time, because + that is almost never a good thing to do. Default is 604800s (one week). +
+ min_ttl=timespec;
+ This option sets the minimum time a record is held in cache. All dns + resource records have a time to live field that says for what period of time the + record may be cached before it needs to be requeried. If this is less than the + value given with min_ttl, this time to live value is set to min_ttl. + Default is 120 seconds. +
+ neg_ttl=timespec;
+ This option sets the time that negatively cached records will remain valid in the + cache if no time to live can be determined. This is always the case when whole + domains are being cached negatively, and additionally when record types are cached + negatively for a domain for which no SOA record is known to pdnsd. If a SOA is present, + the ttl of the SOA is taken. +
+ neg_rrs_pol=(on|off|auth|default);
+ This sets the RR set policy for negative caching; this tells pdnsd under which circumstances + it should cache a record type negatively for a certain domain. off will + turn the negative caching of record types off, on will always add a negative + cache entry when a name server did not return a record type we asked it for, and auth + will only add such entries if the answer came from an authoritative name server for that + domain.
+ New in version 1.2.8: The default setting will add a negatively cached record + if either the answer was authoritive or the answer indicated the name server had "recursion available" + while the query explicitly requested such recursion.
+ The preset is "default" (used to be auth). +
+ neg_domain_pol=(on|off|auth);
+ This is analogue to neg_rrs_pol for whole domain negative caching. It should be safe + to set this on, because I have not seen a caching server that will falsely claim that a + domain does not exist.
+ The default is auth. +
+ run_as=string;
+ This option allows you to let pdnsd change its user and group id after operations that needed + privileges have been done. This helps minimize security risks and is therefore recommended. The + supplied string gives a user name whose user id and primary group id are taken.
+ A little more details: after reading the config file, becoming a daemon (if specified) and starting + the server status thread, the main thread changes its gid and uid, as do all newly created threads + thereafter. By taking another uid and gid, those threads run with the privileges of the + specified user. + Under Linux and FreeBSD, the server status thread runs with the original privileges only when the strict_setuid option + is set to off (see below, on by default), because these may be needed + for exec uptests. The manager thread also retains its original privileges in this case. + You should take care that the user you specify has write permissions on your cache file and + status pipe (if you need a status pipe). You should look out for error messages like "permission denied" + and "operation not permitted" to discover permission problems.
+
+ strict_setuid=(on|off);
+ When used together with the run_as option, this option lets you specify that all threads of the + program will run with the privileges of the run_as user. This provides higher security than + the normal run_as + option, but is not always possible. See the run_as option for further discussion.
+ This option is on by default.
+ Note that this option has no effect on Non-Linux systems. +
+ paranoid=(on|off);
+ Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers to query other servers themselves + if possible, + and to give back answers for domains that may not be in its authority), and accepts additional records with information + for servers that are not in the authority of the queried server. This opens the possibility of so-called cache poisoning: + a malicious attacker might set up a dns server that, when queried, returns forged additional records. This way, he might + replace trusted servers with his own ones by making your dns server return bad IP addresses. This option protects + you from cache poisoning by rejecting additional records + that do not describe domains in the queried servers authority space and not doing recursive queries any more. + An exception + to this rule are the servers you specify in your config file, which are trusted.
+ The penalty is a possible performance decrease, in particular, more queries might be necessary for the same + operation.
+ You should also notice that there may be other similar security problems, which are essentially problems of + the DNS, i.e. + any "traditional" server has them (the DNS security extensions solve these problems, but are not widely + supported). + One of this vulnerabilities is that an attacker may bombard you with forged answers in hopes that one may match a + query + you have done. If you have done such a query, one in 65536 forged packets will be succesful (i.e. an average packet + count of 32768 is needed for that attack). pdnsd can use TCP for queries, + which has a slightly higher overhead, but is much less vulnerable to such attacks on sane operating systems. Also, pdnsd + chooses random query ids, so that an attacker cannot take a shortcut. If the attacker is able to listen to your network + traffic, this attack is relatively easy, though.
+ This vulnerability is not pdnsd's fault, and is possible using any conventional + name server (pdnsd is perhaps a little more secured against this type of attacks if you make it use TCP).
+ The paranoid option is off by default.
+
+ ignore_cd=(on|off);
+ New in version 1.2.8: This option lets you specify that the CD bit of a DNS query will be ignored. + Otherwise pdnsd will reply FORMERR to clients that set this bit in a query. + It is safe to enable this option, as the CD bit refers to 'Checking Disabled' + which means that the client will accept non-authenticated data.
+ This option is on by default. Turn it off if you want the old behaviour (before version 1.2.8). +
+ scheme_file=string;
+ In addition to normal uptests, you may specify that some servers shall only be queried when a certain + pcmcia-cs scheme is active (only under linux). For that, pdnsd needs to know where the file resides that + holds the pcmcia scheme information. Normally, this is either /var/lib/pcmcia/scheme or + /var/state/pcmcia/scheme. +
+ status_ctl=(on|off);
+ This has the same effect as the -s command line option: the status control is enabled when + on is specified.
+ Added by Paul Rombouts: Note that pdnsd-ctl allows run-time configuration of pdnsd, + even the IP addesses of the name servers can be changed. If you're not using pdnsd-ctl and + you want maximum security, you should not enable this option. It is disabled by default. +
+ daemon=(on|off);
+ This has the same effect as the -d command line option: the daemon mode is enabled when + on is specified.
+ Default is off. +
+ tcp_server=(on|off);
+ tcp_server=on has the same effect as the -t or --tcp + command-line option: it enables TCP serving. + Similarly, tcp_server=off is like the --notcp command-line option.
+ Default is on. +
+ pid_file=string;
+ This has the same effect as the -p command line option: you can specify a file that pdnsd + will write its pid into when it starts in daemon mode. +
+ verbosity=number;
+ This has the same effect as the -v command line option: you can set the verbosity of pdnsd's + messages with it. The argument is a number between 0 (few messages) to 3 (most messages). +
+ query_method=(tcp_only|udp_only|tcp_udp|udp_tcp);
+ This has the same effect as the -m command line option. + Read the documentation for the command line option on this. + tcp_only corresponds to the to, udp_only to the uo, + tcp_udp to the tu and udp_tcp to the ut + argument of the command line option.
+ If you use query_method=tcp_udp, it is recommended that you also set the global timeout option to at least twice the longest server timeout. +
+ run_ipv4=(on|off);
+ This has the same effect as the -4 or -6 command line option: + if on is specified, IPv4 support is enabled, and IPv6 support is disabled (if available). + If off is specified, IPv4 will be disabled and IPv6 will be enabled. + For this option to be meaningful, pdnsd needs to be compiled with support for the protocol you choose. + If pdnsd was compiled with both IPv4 and IPv6 support, and you want to include IPv6 addresses + in the configuration file, you will probably need to specify run_ipv4=off first to + ensure that the IPv6 addresses are parsed correctly. +
+ debug=(on|off);
+ This has the same effect as the -g command line option: the debugging messages are enabled when + on is specified. +
+ ctl_perms=number;
+ This option allows you to set the file permissions that the pdnsd status control socket will have. These + are the same as file permissions. The owner of the file will be the run_as user, or, if none is specified, + the user who started pdnsd. If you want to specify the permissions in octal (as usual), don't forget + the leading zero (0600 instead of 600!). To use the status control, write access is needed. The default + is 0600 (only the owner may read or write).
+ Please note that the socket is kept in the cache directory, and that the cache directory permissions + might also need to be adjusted. Please ensure that the cache directory is not writeable for untrusted + users. +
+ proc_limit=number;
+ With this option, you can set a limit on the pdnsd threads that will be active simultaneously. If + this number is exceeded, queries are queued and may be delayed some time. + See also the procq_limit option.
+ The default for this option is 40. +
+ procq_limit=number;
+ When the query thread limit proc_limit is exceeded, connection attempts to pdnsd will be queued. + With this option, you can set the maximum queue length. + If this length is also exceeded, the incoming queries will be dropped. + That means that tcp connections will be closed and udp queries will just be dropped, which + will probably cause the querying resolver to wait for an answer until it times out.
+ See also the proc_limit option. A maximum of proc_limit+procq_limit + query threads will exist at any one time (plus 3 to 6 threads that will always + be present depending on your configuration).
+ The default for this option is 60. +
+ tcp_qtimeout=timespec;
+ This option sets a timeout for tcp queries. If no full query has been received on a tcp connection + after that time has passed, the connection will be closed. The default is set using the + --with-tcp-qtimeout option to configure. +
+ par_queries=number;
+ This option used to set the maximum number of remote servers that would be queried simultaneously, + for every query that pdnsd receives.
+ Since version 1.1.11, the meaning of this option has changed slightly. + It is now the increment with which the number of parallel queries is + increased when the previous set of servers has timed out. + For example, if we have a list server1, server2, server3, etc. of available servers + and par_queries=2, then pdnsd will first send queries to server1 and server2, + and listen for responses from these servers.
+ If these servers do not send a reply within their timeout period, pdnsd will send additional + queries to server3 and server4, and listen for responses from + server1, server2, server3 and server4, and so on until a useful reply is + received or the list is exhausted.
+ In the worst case there will be pending queries to all the servers in the list of available servers. + We may be using more system resources this way (but only if the first servers in the list + are slow or unresponsive), but the advantage is that we have a greater chance of catching a reply. + After all, if we wait longer anyway, why not for more servers.
+ See also the explanation of the global timeout option below.
+ 1 or 2 are good values for this option. + The default is set at compile time using the --with-par-queries option to configure. +
+ timeout=timespec;
+ This is the global timeout parameter for dns queries. + This specifies the minimum period of time pdnsd will wait after sending the + first query to a remote server before giving up without having + received a reply. The timeout options in the configuration file are + now only minimum timeout intervals. Setting the global timeout option + makes it possible to specify quite short timeout intervals in the + server sections (see below). This will have the effect that pdnsd will start + querying additional servers fairly quickly if the first servers are + slow to respond (but will still continue to listen for responses from + the first ones). This may allow pdnsd to get an answer more quickly in + certain situations.
+ If you use query_method=tcp_udp it is recommended that + you make the global timeout at least twice as large as the largest + server timeout, otherwise pdnsd may not have time to try a UDP query + if a TCP connection times out.
+ Default value is 0. +
+ randomize_recs=(on|off);
+ If this option is turned on, pdnsd will randomly reorder the cached records of one type + when creating an answer. This supports round-robin DNS schemes and increases fail + safety for hosts with multiple IP addresses, so this is usually a good idea.
+ On by default. +
+ query_port_start=(number|none);
+ If a number is given, this defines the start of the port range used for queries of pdnsd. The + value given must be >= 1024. The purpose of this option is to aid certain firewall + configurations that are based on the source port. Please keep in mind that another application + may bind a port in that range, so a stateful firewall using target port and/or process uid may + be more effective. In case a query start port is given pdnsd uses this port as the first port of a + specified port range (see query_port_end) used for queries. + pdnsd will try to randomly select a free port from this range as local port for the query.
+ To ensure that there are enough ports for pdnsd to use, the range between query_port_start and + query_port_end should be adjusted to at least (par_queries * proc_limit). + A larger range is highly recommended for security reasons, and also because other applications may + allocate ports in that range. If possible, this range should be kept out of the space + that other applications usually use.
+ The default for this option is 1024. Together with the default value of query_port_end, + this makes it the hardest for an attacker to guess the source port used by the pdnsd resolver. + If you specify none here, pdnsd will let the kernel choose the source port, but + this may leave pdnsd more vulnerable to an attack. +
+ query_port_end=number;
+ Used if query_port_start is not none. Defines the last port of the range started by query_port_start + used for querys by pdnsd. The default is 65535, which is also the maximum legal value for this option. + For details see the description of query_port_start. +
+ delegation_only=string;
+ Added by Paul Rombouts: This option specifies a "delegation-only" zone. + This means that if pdnsd receives a query for a name that is in a + subdomain of a "delegation-only" zone but the remote name server + returns an answer with an authority section lacking any NS RRs for + subdomains of that zone, pdnsd will answer NXDOMAIN (unknown domain). + This feature can be used for undoing the undesired effects of DNS + "wildcards". Several "delegation-only" zones may be specified together. + If you specify root servers in a server section it is + important that you set root_server=on in such a section.
+ Example: +

delegation_only="com","net";

+ This feature is off by default. It is recommended that you only use + this feature if you actually need it, because there is a risk that + some legitimate names will be blocked, especially if the remote + name servers queried by pdnsd return answers with empty authority + sections. +
+ ipv4_6_prefix=string;
+ This option has the same effect as the -i command-line option. + When pdnsd runs in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 addresses in + the configuration file (or addresses specified with pdnsd-ctl) + to IPv6-mapped addresses. + The string must be a valid IPv6 address. Only the first 96 bits are used. + Note that this only effects the parsing of IPv4 addresses listed after this option.
+ The default is "::ffff.0.0.0.0". +
+ use_nss=(on|off);
+ If this option is turned on, pdnsd will call initgroups() to set up the group access list, + whenever pdnsd changes its user and group id (see run_as option). + There is a possible snag, though, if initgroups() uses NSS (Name Service Switch) and + NSS in turn uses DNS. In such a case you may experience lengthy timeouts and stalls. + By setting use_nss=off, you can disable the initgroups() call + (only possible in versions 1.2.5 and later).
+ This option was contributed by Jan-Marek Glogowski.
+ On by default. +
+ udpbufsize=number;
+ New in version 1.2.9: + This option sets the upper limit on the size of UDP DNS messages. The default is 1024.
+ See also the edns_query server option below. +
+ +
+

2.1.2 server Section

+ Each server section specifies a set of name servers that pdnsd should try to get + resource records or authoritative name server information from. The servers are + queried in the order of their appearance (or parallel to a limited extend). + If one fails, the next one is taken and so on.
+ You probably want to specify the dns server in your LAN, the caching dns servers + of your internet provider or even a list of root servers in one or more server sections.
+ The supported options in this section are:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ label=string;
+ Specify a label for the server section. This can be used to refer to this section + when using pdnsd-ctl, the pdnsd control utility.
+ You can give several server sections the same label, but if you want to change the addresses + of a server section (see ip option below) during run-time with + "pdnsd-ctl server label up dns1,dns2,...", + the label must be unique. +
+ ip=string;
+ Give the IP (the address, not the host name) of the server.
+ Multiple IP addresses can be given per server section. + This can be done by entering multiple lines of the form ip=string; + or a single line like this: +

ip=string,string,string;

+ IP addresses do not have to be specified in the configuration file. + A server section without IP addresses will remain inactive until it is assigned + one or more addresses with pdnsd-ctl, + the pdnsd control utility.
+ If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses you specify + here will be skipped with a warning message, unless pdnsd is running in IPv6 mode. + Thus, unless pdnsd was compiled to startup in IPv6 mode by default, you need to use the + command-line option -6 or set run_ipv4=off + first (see global section) in order to ensure + that IPv6 addresses are parsed correctly.
+ If pdnsd is running in IPv6 mode and you specify an IPv4 address here, + it will automatically be mapped to an IPv6 address. +
+ file=string;
+ New in version 1.2: This option allows you to give the name of a resolv.conf-style file. + Of the lines beginning with the nameserver keyword, the second field will be parsed as an + IP address, as if it were specified with the ip= option. The remaining lines will be ignored. + If the contents of the file changes while pdnsd is running, you can make pdnsd aware of the changes through the + use of pdnsd-ctl, the pdnsd control utility. + This is usually most conveniently done by placing the command "pdnsd-ctl config" in a script + that is automatically run whenever the DNS configuration changes.
+ For example, suppose you have a ppp client that writes the DNS configuration for your ISP to the file + /etc/ppp/resolv.conf and runs the script /etc/ppp/ip-up when a new + connection is established. One way of ensuring that pdnsd is automatically reconfigured is to + add a server section in the config file with file=/etc/ppp/resolv.conf and to + add the command "pdnsd-ctl config" to /etc/ppp/ip-up. +
+ port=number;
+ Give the port the remote name server listens on. Default is 53 (the official + dns port) +
+ uptest=(ping|none|if|dev|diald|exec|query);
+ Determine the method to check whether the server is available. Currently + defined methods are: + +
    +
  • ping: Send an ICMP_ECHO request to the server. If it doesn't respond + within the timeout, it is regarded to be unavailable until the next probe. +
  • none: The availability status is not changed, only the time stamp is updated. +
  • if: Check whether the interface (specified in the interface= option) is + existent, up and running. This currently works for all "ordinary" + network interfaces, interfaces that disappear when down (e.g. ppp?), + and additionally for Linux isdn interfaces (as of kernel 2.2). Note that + you need a /dev/isdninfo device file (major#45, minor#255), or the + isdn uptest will always fail. +
  • dev and diald: Perform an if uptest, and, if that + was succesful, additionally check whether a program is running that + has locked a given (modem-) device. The needed parameters are an interface (specified as for the if + uptest, e.g. "ppp0") and a device relative to /dev (e.g. + "modem" for /dev/modem specified using the device= option. + pdnsd will then look for a pid file for the given interface in /var/lock (e.g. + /var/run/ppp0.pid) and for a lockfile for the given device (e.g. /var/lock/LCK..modem), + and then test whether the locking process is the process that created the pid file and this process is still + alive. If this is the case, the normal if uptest is executed for the given interface.
    + The dev option is for pppd dial-on-demand, diald is the same for diald users. +
  • exec: Executes a given command in the /bin/sh shell + (as /bin/sh -c <command>) + and evaluates the result (the return code of the last command) in the shell's way of handling return codes, + i.e. 0 indicates success, all other indicate failure. The shell's process name will be + uptest_sh. The command is given with the uptest_cmd option (see below). + For secuity issues, also see that entry. +
  • query: New in version 1.2: + This works like the ping test, except it sends an (empty) DNS query to the remote server. + If the server sends a well-formed response back within the timeout period (except SERVFAIL), + it will be regarded as available. + This test is useful if a remote server does not respond to ICMP_ECHO requests at all, + which unfortunately is quite common these days. + It can also happen that a remote server is online but ignores empty DNS queries. + Then you will need the set the query_test_name option (see below). + In many cases this test will be a more reliable indicator of availability + than the ones mentioned before. +
+

+ The default value is none. +

+ NOTE: If you use on-demand dialing, use none, if, + dev, diald or exec, + since ping or query will send packets + in the specified interval and the interface will thus frequently dial! +

+ ping_timeout=number;
+ Sets the timeout for the ping test in tenths of seconds + (this unit is used for legacy reasons; actually the current implementation is + only accurate to a second).
+ The default is 600 (one minute). +
+ ping_ip=string;
+ The IP address for the ping test. The default is the IP of the name server. +
+ query_test_name=string;
+ New in version 1.2.9: + Sets the name to be queried when using uptest=query availability test. + If the string is the unquoted constant none, + an empty query is used (this the default), otherwise a query of type A will be + sent for the domain name specified here. It is not necessary for the domain name + to exist or have a record of type A in order for the uptest to succeed.
+ If the the remote server ignores empty queries, you will probably want to set + query_test_name="." (the root domain). +
+ uptest_cmd=string,string;
+ or
+ uptest_cmd=string;
+ Sets the command for the uptest=exec function to the first string. + If the second string is given, it specifies a user with whose user + id and primary group id the command is executed.
+ This is especially useful if you are executing the server as root, + but do not want the uptest to be performed with root privileges. + In fact, you should never execute the uptest as root if you can help + it.
+ If the server is running setuid or setgid, the privileges thus gained + are attempted to be dropped even before changing identity to the + specified user to prevent setuid/gid security holes (otherwise, any + user might execute commands as root if you setuid the executable).
+ Note that this is not always possible, and that pdnsd should never + be installed as setuid or setgid. + The command is executed using /bin/sh, so you should be able to use + shell builtin commands. +
+ interval=(timespec|onquery|ontimeout);
+ Sets the interval for the server up-test. The default is 900 seconds; + however, a test is forced when a query times out and the timestamp is reset then.
+ If you specify onquery instead of a timeout, the interface will be + tested before every query. This is to prevent automatically dialing + interfaces (diald/pppd or ippp) to dial on dns queries. It is intended to be + used in connection with an interface-testing uptest ;-)
+ Note that using uptest=exec, you might run into performance problems + on slow machines when you use that option. + DON'T use onquery with uptest=ping or + uptest=query, as it may cause delays if the server does not answer + (btw, it doesn't make sense anyway). + Note also that using onquery is no guarantee that the interface + will not be used. When another (reachable) dns server tells pdnsd + to query a third dns server for data, pdnsd will do that and has + no means of checking whether this will dial up the interface or not. + This however should be a rare situation.
+ New in version 1.2.3: + A third possibility is to specify interval=ontimeout. + In this case the server is not tested at startup/reconfiguration, nor at regular intervals, + but only after a DNS query to a server times out. Certain types of network problems + such as a refused connection will also cause the server to be considered unavailable. + However, once a server is declared dead it is never considered again unless it is revived using a + pdnsd-ctl config or server command. + The idea behind this option is to minimize uptests by assuming all + servers are available until there is reason to believe otherwise. +
+ interface=string;
+ The network interface (or network device, e.g. "eth0") for the uptest=if option. + Must be specified if uptest=if is given. +
+ device=string;
+ The (modem-) device that is used for the dev uptest. If you use this for a dial-on-demand + ppp uptest (together with uptest=dev), you need to enter the device you are using for your + pppd here, e.g. modem for /dev/modem.
+ Must be specified if uptest=dev is given. +
+ timeout=timespec;
+ Set the timeout for the dns query. The default is 120 seconds. You probably want to set this lower.
+ Timeouts specified in the configuration file are only treated as the + minimum period of time to wait for a reply. A queries to a remote + server are not canceled until a useful reply has been received, or all + the other queries have timed out or failed.
+ If you have also set the global timeout option, you may consider setting a fairly small value here. + See the explanation of the timeout option in the global + section for what that means. +
+ purge_cache=(on|off);
+ In every fetched dns record, there is a cache timeout given, which + specifies how long the fetched data may be cached until it needs to be + reloaded. If purge_cache is set to off, the stale records are not purged + (unless the cache size would be exceeded, in this case the oldest records are purged). + Instead, they are still served if they cannot succesfully be + updated (e.g. because all servers are down).
+ Default is off. +
+ caching=(on|off);
+ Specifies if caching shall be performed for this server at all. Default is + on. +
+ lean_query=(on|off);
+ Specifies whether to use the "lean" query mode. In this mode, only the + information actually queried from pdnsd is resolved and cached. This has + the advantage that usually less cache space is used and the query is + usually faster. In 90% of the cases, only address (A) records are needed + anyway. If switched off, pdnsd will always cache all data about a host + it can find and will specifically ask for all available records + (well, at least it is a good approximation for what it really does ;-) + This will of course increase the answer packet sizes.
+ Some buggy name servers may not deliver CNAME records when not asked for + all records. I do not know if such servers are around, but if you have + trouble resolving certain host names, try turning this option off.
+ A last note: If you use multiple pdnsd's that access each other, turning + this option on is probably a big win.
+ This on by default. +
+ edns_query=(on|off);
+ New in version 1.2.9: + Specifies whether to use EDNS (Extension mechanisms for DNS) for outgoing queries. + Currently this is only useful for allowing UDP message sizes larger than 512 bytes. + Note that setting this option on can give problems in combination with some legacy + systems or software, including, embarrassingly enough, previous versions of pdnsd.
+ The default is off, but if your network can handle UDP payloads + significantly larger than 512 bytes, the recommended value is on.
+ Note that this option only effects outgoing queries. If pdnsd receives a query using + EDNS, it will reply using EDNS regardless of the value of this option. +
+ See also the udpbufsize option above. +
+ scheme=string;
+ You can specify a pcmcia-cs scheme that is used in addition to the uptests. If you specify + a scheme here, the server this section is for will only be queries if the given scheme + is active. Shell wildcards (* and ?) are allowed in the string under their special + meanings. You need to use the scheme_file option on the global + section to make this option work. +
+ preset=(on|off);
+ This allows you to specify the initial state of a server before any uptest is performed. + on specifies that the server is regarded available. The default is on. + This is especially useful when you set uptest=none; and want to change + the status of a server only via pdnsd-ctl. +
+ proxy_only=(on|off);
+ When this option is set to on, answers given by the servers are always accepted, and no + other servers (as, for example, specified in the NS records of the query domain) are + queried. If you do not turn this option on, pdnsd will do such queries in some cases + (in particular when processing ANY queries).
+ This option is useful when you do not want pdnsd to make connections to outside servers + for some reasons (e.g. when a firewall is blocking such queries).
+ I recommend that you turn on lean_query when using this option.
+ Default is off. +
+ root_server=(on|off|discover);
+ Set this option to on if the servers specified in a section are root servers. + A root server will typically only give the name servers for the top-level domain in its reply. + Setting root_server=on will cause pdnsd to try to use cached information about + top-level domains to reduce to number of queries to root servers, making the resolving of + new names more efficient. + You can get a list of available root servers by running the command + "dig . ns".
+ This option is also necessary if you use the delegation_only option.
+ New in version 1.2.8: This option may also be set to "discover". + This will cause pdnsd to query the servers provided with the ip= option + to obtain the full list of root servers. The root-server addresses will replace the addresses + specified with the ip= option. + This will only be done once on startup, or after a "pdnsd-ctl config" command. + In this case the name servers specified with the ip= option don't have to be + root servers, they just have to know the names and addresses of the root servers. + After root-server discovery pdnsd will behave just as if root_server=on + had been specified.
+ Default is off. +
+ randomize_servers=(on|off);
+ New in version 1.2.6: Set this option to on to give each name server + in this section an equal chance of being queried. If this option is off, the name servers + are always queried starting with the first one specified. Even with this option on, the + query order is not truly random. Only the first server is selected randomly; the following + ones are queried in consecutive order, wrapping around to the beginning of the list when + the end is reached. Note that this option only effects the order within a section. The + servers in the first (active) section are always queried before those in the second one, + etc.
The default is off, but if you are resolving from root servers setting this + option on is highly recommended. If root_server=on this option also effects + the query order of the name servers for the top-level domains. +
+ reject=string;
+ New in version 1.2.6: This option can be used to make pdnsd reject replies that + contain certain IP addresses. You can specify a single IP address, which will be matched + exactly, or a range of addresses using an address/mask pair. + The mask can be specified as a simple integer, indicating the number of initial 1 bits in + the mask, or in the usual IP address notation. IP addresses may be either IPv4 or IPv6 + (provided there is sufficient support in the C libraries and support for AAAA records was + not disabled). + When addresses in the reject list are compared with those in a reply, only the bits + corresponding to those set in the netmask are significant, the rest are ignored.
+ Multiple addresses or address/mask pairs may be specified; this can be done by entering + multiple lines of the form reject=string; + or a single line like this: +

reject=string,string,string;

+ How pdnsd reacts when an address in the reply matches one in the reject list, + depends on the reject_policy option, see below. +
+ reject_policy=(fail|negate);
+ New in version 1.2.6: + This option determines what pdnsd does when an address in the reply from a name server + matches the reject list (see above). If this option is set to + fail, pdnsd will try another server, or, if there no more servers to try, + return the answer SERVFAIL. If this option is set to negate, pdnsd will + immediately return the answer NXDOMAIN (unknown domain) without querying additional + servers. The fail setting is useful if you don't always trust the servers in + this section, but do trust the servers in the following section. The negate + setting can be used to completely censor certain IP addresses. In this case you should put + the same reject list in every server section, and also set the + reject_recursively option (see below) to true.
+ The default is fail. +
+ reject_recursively=(on|off);
+ New in version 1.2.6: Normally pdnsd checks for addresses in the + reject list (see above) only when the reply comes directly from a name server + listed in the configuration file. With this option set to on, pdnsd will + also do this check for name servers that where obtained from NS records in the authority + section of a previous reply (which was incomplete and non-authoritative).
+ Default is off. +
+ policy=(included|excluded|simple_only|fqdn_only);
+ pdnsd supports inclusion/exclusion lists for server sections: with include= + and exclude= (see below) you can specify domain names for which this server + will be used or will not be used. The first match counts (i.e., the first include or + exclude rule in a server section that matches a domain name is applied, and the + search for other rules is terminated). If no rule matched a given domain name, + the policy= option determines whether this server is used for the + lookup for that domain name; when included is given, the server will + be asked, and when excluded is given, it will not. + If simple_only is given the server will be used if the name to lookup + is a simple (single-label) domain name, on the other hand if fqdn_only + is given the server will be used only for names consisting of two or more labels + (i.e. the name has at least one dot in-between).
+ If no server is available for a queried domain, pdnsd will return an error message + to the client that usually will stop the client's attempts to resolve a specific + domain from this server (the libc resolver will e.g. return an error to the application that + tried to resolve the domain if no other servers are available in the resolv.conf). + This may be of use sometimes.
+ Note: the simple_only and fqdn_only constants + were added by Paul Rombouts. + They are useful for controlling which name servers (if any) will be used by + pdnsd for resolving simple (single-label) host names. + fqdn_only used to stand for "fully qualified domain name only", but this is + actually a misnomer. The names in queries received by pdnsd are always considered to be + fully qualified. If you do not exactly understand what the options simple_only and + fqdn_only are good for, you are probably better off not using them.
+ The default for this option is included. +
+ include=string;
+ This option adds an entry to the exclusion/inclusion list. If a domain matches + the name given as string, the server is queried if this was the first matching rule + (see also the entry for policy).
+ If the given name starts with a dot, the whole subdomain + of the given name including the one of that name is matched, e.g. ".foo.bar." + will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar.
+ If it does not start in a dot, only exactly the given name (ignoring the case, of course) + will be matched (hint: if you want to include all subdomains, but not the domain of the given + name itself, place an exact-match exclude rule before the include rule, e.g: + exclude="foo.bar."; include=".foo.bar.";
+ Previous versions of pdnsd + required that names given with this and the next option ended in a dot, but since + version 1.1.8b1-par8, pdnsd automatically adds a dot at the end if it + is missing.
+ pdnsd now also accepts a more compact notation for adding several "include" entries in + one line, e.g.: +

include=".foo",".bar",".my.dom";

+
+ exclude=string;
+ This option adds an entry to the exclusion/inclusion list. If a domain matches + the name given as string, the server is not queried if this was the first matching rule + (see also the entry for policy).
+ If the given name starts with a dot, the whole subdomain + of the given name including the one of that name is matched, e.g. ".foo.bar." + will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar.
+ If it does not start in a dot, only exactly the given name (ignoring the case, of course) + will be matched (hint: if you want to exclude all subdomains, but not the domain of the given + name itself, place an exact-match include rule before the exclude rule, e.g: + include="foo.bar."; exclude=".foo.bar.";
+ pdnsd now also accepts a more compact notation for adding several "exclude" entries in + one line, e.g.: +

exclude=".foo",".bar",".my.dom";

+
+
+

2.1.3 rr Section

+ Every rr section specifies a dns resource record that is stored locally. It + allows you to specify own dns records that are served by pdnsd in a limited way. + Only A, PTR, CNAME, MX, NS and SOA records are implemented.
+ This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. + and localhost. (and perhaps even one or two hosts) without having to start an + extra named if your cached name servers do not serve those records. + It is NOT intended and not capable to work as a full-featured name server. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ name=string;
+ Specifies the name of the resource records, i.e. the domain name of + the resource the record describes. This option must be specified + before any a, ptr, cname, + mx, ns or soa records. + Names are interpreted as absolute domain names + (i.e. pdnsd assumes they end in the root domain). + For this and all following arguments that take domain names, you need to + specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing.
+ New in version 1.2: It is also possible to specify a name starting + with the label *. Such a name is called a wildcard. The * in a wildcard + can match one or more labels in a queried name, but only whole labels. + Any other * characters in a wildcard, apart from the leading one, + will only match a literal *.
+ For example, *.mydomain will match a.mydomain or www.a.mydomain, but not + mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. + *a.mydomain will only match itself.
+ Before you can specify an rr section with name=*.mydomain + you must define some records for mydomain, typically NS and/or SOA records. + Example: +
+    rr {
+	name = mydomain;
+	ns = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+    rr {
+	name = *.mydomain;
+	a = 192.168.1.10;
+    }
+ In this example, www.mydomain and ftp.mydomain will resolve to the numeric + address 192.168.1.10 (unless you add rr sections explicitly + specifying different addresses for www.mydomain or ftp.mydomain). + If you want mydomain also to resolve to a numeric address, + add an A record to the first rr section. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after this entry. + This may be redefined. The default is 86400 seconds (=1 day). +
+ authrec=(on|off);
+ If this is turned on, pdnsd will create authoritative local records for this rr section. + This means that pdnsd flags the domain record so that records of this domain that are not + present in the cache are treated as non-existent, i.e. no other servers are queried for + that record type, and an response containing none of those records is returned. This is + most time what people want: if you add an A record for a host, and it has no AAAA record + (thus no IPv6 address), you normally don't want other name servers to be queried for it.
+ This is on by default.
+ Please note that this only has an effect if it precedes the name option! +
+ reverse=(on|off);
+ New in version 1.2: If you want a locally defined name to resolve to a numeric address + and vice versa, you can achieve this by setting reverse=on before defining the A record + (see below). The alternative is to define a separate PTR record, but you will + probably find this option much more convenient.
+ The default is off. +
+ a=string;
+ Defines an A (host address) record. The argument is an IPv4 address in dotted notation. + pdnsd will serve this address for the host name given in the name option.
+ Provided there is sufficient support in the C libraries and support for AAAA records was not + disabled, the argument string may also be an IPv6 address, in which case an AAAA record + will be defined.
+ This option be may used multiple times within an rr section, causing + multiple addresses to be defined for the name. However, if you put the different addresses + in different rr sections for the same name, the definition in the last + rr section will cancel the definitions in the previous ones. +
+ ptr=string;
+ Defines a PTR (domain name pointer) record. The argument is a host name in + dotted notation (see name). The ptr record is for resolving adresses into names. For example, if + you want the adress 127.0.0.1 to resolve into localhost, and localhost into 127.0.0.1, you need something + like the following sections:
+
+    rr {
+	name = localhost;
+	a = 127.0.0.1;
+	owner = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+    rr {
+	name = 1.0.0.127.in-addr.arpa;
+	ptr = localhost;
+	owner = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+ The second section is for reverse resolving and uses the ptr option. + Note that you can get the same effect by specifying only the first rr section + with reverse=on.
+ There is something special about the name in the second section: + when a resolver wants to get a host name from an internet address, + it composes an address that is built of the IP address in reverse byte order + (1.0.0.127 instead of 127.0.0.1) where each byte of the adress written + as number constitutes a sub-domain under the domain in-addr.arpa.
+ So, if you want to compose an adress for reverse resolving, take your ip in dotted notation (e.g. 1.2.3.4), + reverse the byte order (4.3.2.1) and append in-addr.arpa. (4.3.2.1.in-addr.arpa.) + Then, define an rr section giving this address as name and the domain name corresponding to + that ip in the ptr option. +
+ cname=string;
+ Defines a CNAME (canonical name) record. + The argument should be a fully-qualified host name in dotted notation (see name). + A CNAME is the DNS equivalent of an alias or symbolic link.
+ A useful application for CNAMEs is giving short, easy to remember nicknames to hosts with complicated names. + For example, you might want the name "news" to refer to your ISP's news server "nntp2.myisp.com". + Instead of adding an A record for "news" with the same address as "nntp2.myisp.com", you could + put in a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news server changes, + there is no need to update the record for "news".
+ To implement this with pdnsd, you could add the following section to your configuration file:
+
+    rr {
+	name = news;
+	cname = nntp2.myisp.com;
+	owner = localhost;
+    }
+
+ mx=string,number;
+ Defines an MX (mail exchange) record. The string is the host name of the mail server in dotted notation (see name). + The number specifies the preference level.
+ When you send mail to someone, your mail typically goes from your E-mail client to an SMTP server. + The SMTP server then checks for the MX record of the domain in the E-mail address. + For example, with joe@example.com, it would look for the MX record for example.com and find + that the name of mail server for that domain is, say, mail.example.com. + The SMTP server then gets the A record for mail.example.com, and connects to the mail server.
+ If there are multiple MX records, the SMTP server will pick one based on the preference level + (starting with the lowest preference number, working its way up).
+ Don't define MX records with pdnsd unless you know what you're doing. +
+ owner=string;
+ or
+ ns=string;
+ Defines an NS (name server) record. Specifies the name of the host which should be authoritative for the records + you defined in the rr section. This is typically the host pdnsd runs on.
+ Note: In previous versions of pdnsd this option had to be specified before + any a, ptr, cname, mx or soa entries. + In version 1.2, the restrictions on this option are same as the options just mentioned, + and it must listed after the name= option. + This can be a pain if you want to use an old config file which specifies owner= + before name= (sorry about that). + Apart from greater consistency, the advantage is that you can now specify as many NS records as you like (including zero). +
+ soa=string,string,number,timespec,timespec,timespec,timespec;
+ This defines a soa (start of authority) record. The first string is the + domain name of the server and should be equal to the name you specified as + owner.
+ The second string specifies the email address of the maintainer of the name + server. It is also specified as a domain name, so you will have to replace the + @ sign in the name with a dot (.) to get the name you have to specify here. + The next parameter (the first number) is the serial number of the record. You + should increment this number if you change the record.
+ The 4th parameter is the refresh timeout. It specifies after what amount + of time a caching server should attempt to refresh the cached record.
+ The 5th parameter specifies a time after which a caching server should attempt + to refresh the record after a refresh failure.
+ The 6th parameter defines the timeout after which a cached record expires if it + has not been refreshed.
+ The 7th parameter is the ttl that is specified in every rr and should be the + same as given with the ttl option (if you do not specify a ttl, use the default 86400). +
+ txt=string,...,string;
+ New in version 1.2.9: + Defines an TXT record. You can specify one or more strings here. +
+
+

2.1.4 neg Section

+ Every neg section specifies a dns resource record or a dns domain that should be + cached negatively locally. Queries for negatively cached records are always answered + immediatley with an error or an empty answer without querying other hosts as long + as the record is valid. The records defined with neg sections remain + valid until they are explicitely invalidated or deleted by the user using + pdnsd-ctl.
+ This is useful if a certain application asks periodically for nonexisting hosts or + RR types and you do not want a query to go out every time the cached record has + timed out. Example: Netscape Communicator will ask for the servers news and mail + on startup if unconfigured. If you do not have a dns search list for your network, + you can inhibit outgoing queries for these by specifying
+
+    neg {
+	name = news;
+	types = domain;
+    }
+    neg {
+	name = mail;
+	types = domain;
+    }
+ in your config file. If you have a search list, you have to repeat that for any + entry in your search list in addition to the entries given above!
+ In versions 1.1.11 and later, if you negate whole domains this way, all subdomains + will be negated as well. Thus if you specify
+ neg {name=example.com; types=domain;} in the + config file, this will also negate www.example.com, xxx.adserver.example.com, etc. +

+ + + + + + + + + + +
+ name=string;
+ Specifies the name of the domain for which negative cache entries are created. + This option must be specified before the types option. + Names are interpreted as absolute domain names (i.e. pdnsd + assumes they end in the root domain). + You need to specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after this entry. + This may be redefined. The default is 86400 seconds (=1 day). +
+ types=(domain|rr_type[,rr_type[,rr_type[,...]]]);
+ Specifies what is to be cached negatively: domain will cache the whole + domain negatively; alternatively, you can specify a comma-separated list of RR types + which are to be cached negatively. You may specify multiple types options, but + domain and the RR types are mutually exclusive.
+ The RR types are specified using their official names from the RFC's in capitals, + e.g. A, CNAME, NS, PTR, MX, + AAAA, ...
+ The command pdnsd-ctl list-rrtypes will give you a complete list + of those types. pdnsd-ctl is built along with pdnsd + and will be installed in the same directory as the pdnsd binary during make install. +
+
+

2.1.5 source Section

+ Every source section allows you to let pdnsd read the records from a file in an + /etc/hosts-like format. pdnsd will generate records to resolve the entries + address from its host name and vice versa for every entry in the file. This is + normally easier than defining an rr for every of your addresses, since localhost + and your other FQDNs are normally given in /etc/hosts.
+ The accepted format is as follows: The #-sign initiates a comment, the rest of + the line from the first occurence of this character on is ignored. Empty lines + are tolerated.
+ The first entry on a line (predeceded by an arbitrary number of tabs and spaces) + is the IP in dotted notation, the second entry on one line (separated by the + first by an arbitrary number of tabs and spaces) is the FQDN (fully qualified + domain name) for that ip. The rest of the line is ignored by default (in the original + /etc/hosts, it may contain information not needed by pdnsd). +

+ + + + + + + + + + + + + + + + +
+ owner=string;
+ Specifies the name of the host pdnsd runs on and that are specified in dns + answers (specifically, nameserver records). + Must be specified before any file entries.
+ Names are interpreted as absolute domain names (i.e. pdnsd + assumes they end in the root domain). + You need to specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after + this entry. This may be redefined. The default is 86400 seconds (=1 day). +
+ file=string;
+ The string specifies a file name. For every file entry in a source section, + pdnsd will try to load the given file as described above. Failure is indicated + only when the file cannot be opened, malformed entries will be ignored. +
+ serve_aliases=(on|off);
+ If this is turned on pdnsd will serve the aliases given in a hosts-style file. + These are the third entry in a line of a hosts-style file, which usually give a "short name" for the host. + This may be used to support broken clients without a proper domain-search option. + If no aliases are given in a line of the file, pdnsd behaves as without this option for this line.
+ This feature was suggested by Bert Frederiks.
+ It is off by default. +
+ authrec=(on|off);
+ If this is turned on, pdnsd will create authoritative local records with the data from the hosts file. + Please see the description of the option of the same name in the rr section for a closer description of + what this means. Please note that this only has an effect for files sourced with file options + subsequent to this option.
+ This is on by default. +
+
+

2.1.6 include Section

+ A configuration file may include other configuration files. + However, only the top-level configuration file may contain global + and server sections, + thus include files are effectively limited to sections that add local definitions to the cache.
+ Include sections currently only have one type of option, which may be given multiple times within a single section. +

+ + + + +
+ file=string;
+ The string specifies a file name. For every file option in an include section, + pdnsd will parse the given file as described above. The file may contain include sections itself, + but as a precaution pdnsd checks that a certain maximum depth is not exceeded to guard against + the possibility of infinite recursion. +
+
+

3 pdnsd-ctl

+

+ pdnsd-ctl allows you to configure pdnsd at run time. To make this work, you have to start pdnsd with the -s + option (or use the status_ctl option in the config file). You also should make sure that you + have appropriate permissions on the control socket (use the ctl_perms option to make this sure) and of your pdnsd + cache directory (pdnsd keeps its socket there). Please make sure the pdnsd cache directory is not writeable for untrusted users!

+

+ pdnsd-ctl accepts two command-line options starting with a dash.
+ -c may be used to specify the cache directory (and takes this as argument). + The default for this setting is the pdnsd default cache directory (specified at compile time). + The cache directory for pdnsd-ctl must be the same pdnsd uses!
+ -q can be used to make the output of pdnsd-ctl less verbose.

+

+ The following table lists the commands that pdnsd-ctl supports. The command must always be + the first command-line option (not starting with a dash), the arguments to the command must follow in the given order.
+ In the following table, keywords are in a normal font, while placeholders are in italics.
+ Alternatives are specified like (alt1|alt2|alt3). + Optional arguments are placed between square brackets [].

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandArgumentsDescription
helpPrint a command summary.
versionPrint version and license info.
status + Print a description of pdnsd's cache status, thread status and configuration. + Also shows which remote name servers are assumed to be available. +
server(index|label)  (up|down|retest)  [dns1[,dns2[,...]]] + Set the status of the server with the given index or label (where the given label + matches the one given with the label option in the respective server section in the config file) + to up or down, or force a retest. The index is assigned in the order of definition in + pdnsd.conf starting with 0. Use the status command to view the indices and labels. + You can specify all instead of an index or label to perform the action for all + servers registered with pdnsd. Example:
+ pdnsd-ctl server 0 retest
+ An optional third argument consisting of a list of IP addresses (separated by commas or + white-space characters) can be given. + This list will replace the previous list of addresses of name servers used by pdnsd in the + specified section of the config file. + For example in the /etc/ppp/ip-up script called by pppd you could + place the following line:
+ pdnsd-ctl server isplabel up $DNS1,$DNS2
+ If white space is used to separate addresses the list will have to be quoted. + Spurious commas and white-space characters are ignored. + The last argument may also be an empty string, in which case the existing IP addresses are + removed and the corresponding server section becomes inactive. +
recordname  (delete|invalidate) + Delete or invalidate the records of the given domain name if it is in the + cache. Invalidation means that the records are marked as timed out, and + will be reloaded if possible (if purge_cache is set to on, they will + be deleted in any case).
+ For local records (i.e., records that were given in the config file + using a rr section, records read from a hosts-style file + and records added using pdnsd-ctl), invalidation has no effect. Deletion + will work, though. Example:
+ pdnsd-ctl record localhost. delete +
sourcefn  owner  [ttl]  [(on|off)]  [noauth] + Load a hosts-style file. Works like using the pdnsd + source configuration section. + owner and ttl are used as in the source section. ttl has a default + of 900 (it does not need to be specified). The next to last argument corresponds + to the serve_aliases option, and is off by default (i.e. if it is not specified). + noauth is used to make the domains non-authoritative - please see + the description of the authrec config file options for a description of what + that means. + fn is the filename. The file must be readable by pdnsd! Example:
+ pdnsd-ctl source /etc/hosts localhost. 900 off +
adda  addr  name  [ttl]  [noauth] + Add a record of the given type to the pdnsd cache, replacing existing + records for the same name and type. The 2nd argument corresponds + to the value of the option in the rr section that is named like + the first argument: a is a record for hostname-to-address mapping, + aaaa is the same thing for IPv6 addresses, and ptr is for address-to-hostname + mapping. See the documentation for the rr section for more details. + In case of A and AAAA records, the addr argument may be a list of IP addresses, + separated by commas or white space, causing multiple addresses to be defined + for the same name. + The ttl is optional, the default is 900 seconds. + noauth is used to make the domains non-authoritative - please see + the description of the authrec config file options for a description of what + that means. + If you want no other record than the newly added in the cache, do
+ pdnsd-ctl record name delete + before adding records. This is also better when overwriting local records. Example:
+ pdnsd-ctl add a 127.0.0.1 localhost. 900 +
addaaaa  addr  name  [ttl]  [noauth]
addptr  host  name  [ttl]  [noauth]
addcname  host  name  [ttl]  [noauth]
addmx  host  name  pref  [ttl]  [noauth]
addns  host  name  [ttl]  [noauth]
negname  [type]  [ttl] + Add a negatively cached record to pdnsd's cache, replacing existing + records for the same name and type. If no type is given, the whole + domain is cached negatively. For negatively cached records, errors are + immediately returned on a query, without querying other servers first. + The ttl is optional, the default is 900 seconds.
+ You can get a list of all types you can pass to this command using + pdnsd-ctl list-rrtypes. The type is treated case-sensitive! + Example:
+ pdnsd-ctl neg foo.bar A 900
+ pdnsd-ctl neg foo.baz 900
+
config[filename] + Reload pdnsd's configuration file.
+ The config file must be owned by the uid that pdnsd had when it was + started, and be readable by pdnsd's run_as uid. If no file name is + specified, the config file used at start-up is reloaded.
+ Note that some configuration changes, like the port or IP address pdnsd listens on, + cannot be made this way and you will receive an error message. + In these cases, you will have to restart pdnsd instead. +
includefilename + Parse the given file as an include file, see the documentation on + include sections for a description + what this file may contain.
+ This command is useful for adding definitions to the cache without reconfiguring pdnsd. +
evalstring + Parse the given string as if it were part of pdnsd's configuration file. + The string should hold one or more complete configuration sections. + However, global and + server sections are not allowed, + just as in include files. + If multiple strings are given, they will be joined using newline chars + and parsed together.
+ This command is useful for adding records interactively to the cache + that cannot be defined using the "pdnsd-ctl add" command, + (e.g. soa records). +
empty-cache[[+|-]name ...] + If no arguments are provided, the cache will be completely emptied, + freeing all existing entries. + Note that this also removes local records, as defined by the config file. + To restore local records, run "pdnsd-ctl config" or + "pdnsd-ctl include filename" immediately afterwards.
+ The "pdnsd-ctl empty-cache" command now accepts additional arguments; + these are interpreted as include/exclude names. If an argument starts with a '+' + the name will be included. If an argument starts with a '-' it will be + excluded. If an argument does not begin with '+' or '-', a '+' is + assumed. If the domain name of a cache entry ends in one of the names in + the list, the first match will determine what happens. If the matching + name is to be included, the cache entry is deleted, otherwise not. + If there are no matches, the default action is not to delete. + Note that if you want to delete exactly one name and no others, you should + use "pdnsd-ctl record name delete", + this is also much more efficient.
+ Examples:
+ pdnsd-ctl empty-cache
+ This command will remove all cache entries.
+
+ pdnsd-ctl empty-cache microsoft.com msft.net
+ This will remove all entries ending in microsoft.com or msft.net.
+
+ pdnsd-ctl empty-cache -localdomain -168.192.in-addr.arpa .
+ This will remove all entries except those ending in localdomain or + 168.192.in-addr.arpa. Note that '.' is the root domain which matches any + domain name. +
dump[name] + Print information stored in the cache about name. + If name begins with a dot and is not the root domain, information about + the names in the cache ending in name (including name without + the leading dot) will be printed. + If name is not specified, information about all the names in the cache will + be printed.
+ For each RR record the time and date that this record has been added to the cache + will be printed in the form mm/dd HH:MM:SS (locally defined records are printed without a time stamp). + After that the type of record is printed with the data. For the more common types + of RR records the data will be printed in human readable form, the remaining ones in a + hexadecimal representation.
+ This command is mainly useful for diagnostic purposes.
+ Note that if you pipe the output of this command through an application that + reads only part of the output and then blocks (such as more or less), + pdnsd will not be able to add new entries to the cache until the pipe is closed. + It is preferable to capture the output in a file in such a case. +
list-rrtypes + List available rr types for the neg command. + Note that those are only used for the neg command, not for add! +
+
+
+

4 contrib/

+ The contrib directory in the pdnsd distribution contains useful user-contributed scripts.
+ So far, there are scripts contributed by Marko Stolle and Paul Rombouts that make pdnsd + usable in a DHCP setup. + Please take a look into the README file in the contrib directory for further information. +
+
+

5 Problems...

+ If you have problems with configuring or running pdnsd, be sure to read the FAQ. + If this does not help you, pdnsd crashes or you find bugs, please mail one of the authors.
+ Note added by Paul A. Rombouts: + Thomas Moestl no longer maintains the code. I have revised the code and added new features. + See README.par and the + ChangeLog in the source directory + (or /usr/share/doc/pdnsd-<version> if you have installed a binary package) + for more details. + If you have questions about my modifications, you can find my email address at the end of + README.par. +
+
+

6 Hacking

+ Here comes some information you might find useful for hacking pdnsd. +
+

6.1 Source files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Makefile.am, configure.in, acconfig.h + autoconf/automake/autoheader scripts. Makefile.am's are in most subdirectories. +
pdnsd.spec.in + A template from which configure generates a spec file for building rpm's for various + distributions. +
version + Contains only the program version string. Needed for several templates. +
src/rc/* + rc (start-up) scripts for various linux distributions. +
src/cache.c + The pdnsd cache subsystem(s) as defined in src/cache.h. + This is the "traditional" pdnsd system which keeps the cache in memory and uses hash tables for accesses. + Sourav K. Mandal is working on a system using gdbm. +
src/pdnsd-ctl/* + Contains the code for pdnsd-ctl, a program that allows you to control pdnsd at run time. +
src/conf-lex.l.in + The lex/flex source file for the config file lexer. This is a template because there might be + inserted "%option yylineno" for proper flex support. + (obsolete, superseded by src/conf-parser.c) +
src/conf-lex.l + This is automatically generated by configure from conf-lex.l.in. It may be overwritten + in any make, so never modify this, but conf-lex.l.in instead! + (obsolete, superseded by src/conf-parser.c) +
src/conf-parse.y + The yacc/bison source of the config file parser. + (obsolete, superseded by src/conf-parser.c) +
src/conf-parser.c, src/conf-parser.h, src/conf-keywords.h + The config file parser written purely in C (versions 1.1.10-par and later). +
src/conff.c, src/conff.h + The configuration handler functions and their prototypes. The parser is called from here. +
src/consts.h + Some constants used by the parser, config file handler functions and in the server status thread, + among others. +
src/dns.c, src/dns.h + Define dns message structures, constants, and some common dns data handlers. dns.h contains gcc-specific + code (in praticular, "__attribute__((packed))"). +
src/dns_answer.c, src/dns_answer.h + Define functions that answer incoming dns queries. +
src/dns_query.c, src/dns_query.h + Define functions to manage outgoing dns queries. +
src/error.c, src/error.h + Functions for error output to stderr or the syslog, and debug output to stderr or pdnsd.debug. +
src/hash.c, src/hash.h + Contains the code for storing and looking up cache entries in the hash table. +
src/helpers.c, src/helpers.h + Define miscellaneous helper functions. +
src/icmp.c, src/icmp.h + Define a function for performing a ping test. This contains OS-specific code. +
src/main.c + Contains main(), which holds the command line parser, performs initialisations and signal handling. +
src/make_hashconvtable.c + Contains the code for the executable make_hashconvtable, which is only run once, during build time, to generate the file hashconvtable.h, used by src/hash.c (versions 1.1.10-par and later). + (obsolete since version 1.2) +
src/make_rr_types_h.pl + A perl script for generating src/rr_types.h, + a C header file containing macro definitions and tables needed for handling the + RR types known to pdnsd, from the text file src/rr_types.in. +
src/rr_types.c, src/rr_types.h, src/rr_types.in + These define tables and macros needed for handling the RR types known to pdnsd. + Since version 1.2.9, rr_types.h is an automatically generated file, + see make_rr_types_h.pl. +
src/netdev.c, src/netdev.h + Define functions for network device handling. OS-specific. +
src/servers.c, src/servers.h + Define functions for the server status thread that performs the periodical uptests. +
src/status.c, src/status.h + Define functions for the status control thread. This is pdnsd's interface to pdnsd-ctl. +
+ +
+
+
+ Copyright (C) 2000, 2001 Thomas Moestl
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2012 Paul A. Rombouts +
+

+ Last revised: 19 April 2012 by Paul A. Rombouts +

+ + diff --git a/app/src/main/jni/pdnsd/doc/html/doc_makefile b/app/src/main/jni/pdnsd/doc/html/doc_makefile new file mode 100644 index 0000000..28fb7d5 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/doc_makefile @@ -0,0 +1,23 @@ + +versionfile = ../../version +arch = i686 +arch2 = x86_64 +extver = _sl6 +system = Scientific Linux 6.2 system + +doc: dl.html +.PHONY: doc clean + +# If the existing dl.html contains '???', then certain packages were missing +# during the previous build and dl.html needs to be built again. +ifneq ($(shell grep -F -l -e '???' dl.html),) +.PHONY: dl.html +endif + +dl.html: %.html: %.html.in htmlsubst.pl $(versionfile) + perl htmlsubst.pl version=`cat $(versionfile)` \ + baseurl='http://members.home.nl/p.a.rombouts/pdnsd/' \ + arch=$(arch) arch2=$(arch2) extver=$(extver) system="$(system)" $< > $@ + +clean: + @rm -fv dl.html diff --git a/app/src/main/jni/pdnsd/doc/html/faq.html b/app/src/main/jni/pdnsd/doc/html/faq.html new file mode 100644 index 0000000..eec6599 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/faq.html @@ -0,0 +1,412 @@ + + + + The pdnsd FAQ + + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

The pdnsd FAQ

+ + + + + + + + + + +
Q: + There are complete and well-tested name servers around, such as the BIND. + These do also perform caching. Why should I use pdnsd? +
A: + pdnsd does not aim to be a complete name server implementation, such as the + BIND. It is optimized for caching, and you can only specify a small subset of all + dns record types pdnsd knows in your local "zone" definitions. + This of course reduces the code size drastically, and such the memory footprint. + There are some features especially interesting for dialin networks, ordinary + (non-server) internet hosts and computers that are often not connected to + to their network, e.g. notebooks (I originally wrote this program for use + with my notebook). + These features are: +
    +
  • permanent disk cache (useful for frequent power-offs/reboots) +
  • usually smaller memory footprint (depends on cache size) (see next question) +
  • offline-detection prevents hangs (e.g. the typical hang on startup of some + Netscape Navigator versions if not dialled in) +
  • better control about timeouts (also to prevent hangs) +
  • better control over the cache +
  • better run-time control +
+
+
+ + + + + + + + + + +
Q: + When I look at the process size with ps, top, gtop, or a similar tool, I see + some processes with a total size well above 3.5 MB. This is much more than + e.g. BIND named (about 1.4 MB). Why? +
A: + Really, it is not. + pdnsd uses multithreading, not multiprocessing. That means that the processes + share most of their process space. In the LinuxThreads library + or NPTL (Native Posix Thread Libary), + which are used by pdnsd on Linux, in fact the total process address space is shared + (although the processes have different stacks, these are in one process + address space). You may check this by looking at the at the process sizes of + the pdnsd threads: all should be the same. The effective size that pdnsd + occupies is thus the size of any of the processes, not the sum of those. + So, pdnsd with empty cache occupies about 800 kB, and the maximum size + should be about the cache size plus this size (in fact, ca 5-10% more). +
+
+ + + + + + + + + + +
Q: + What do I need the status control (option -s) for? +
A: + It enables you to do some things you might or might not need. With it, you can: +
    +
  • query pdnsd's settings at runtime to debug configuration files and + see which servers are regarded to be available +
  • mark servers as available or unavailable, or force a status retest - very + handy if you want to control which servers pdnsd queries, e.g for muliple + dial-up accounts +
  • delete, invalidate or add DNS records - useful e.g. when you want to build + records for dynamically assigned IP addresses or domain names +
  • reload pdnsd's configuration file without restarting pdnsd +
  • print information about the contents of pdnsd's cache. +
+
+
+ + + + + + + + + + +
Q: + What do I need local records (rr- and source-sections in the config file) for? +
A: + Some resolver programs, e.g. nslookup, want to look up the name of the + server they are using before doing anything else. This option is for defining + a PTR record for your IP such that those programs get an answer even if the + name server you are caching is not available or does not offer these records. + By extension, you may also define A and SOA records. This allows you to build + very small zones without having to use a "big" name server. It is NOT + intended to replace such a complete server in anything but VERY small + networks. Alternatively, you may start a named on another host or on the + same host on another port and cache it with pdnsd in addition to other (more + distant) name servers. +
+ The source section allows you to let pdnsd read in your + /etc/hosts file on startup and serve its contents. This file is used by your local + resolver before it even tries the name servers and usually contains + fully-qualified domain names (FQDNs) for all of the internet addresses your host has. + If you source this file, you usually won't need any additional rr sections. Sourcing it also allows + other hosts (eg. in your local network) to access the names defined in your + hosts file. You can of course just add other hosts in your local network to the + servers hosts file, thus making them known to your server's resolver + and pdnsd (if you sourced that file). +
+ If you don't know what this answer was all about, you should just take the + source section in the sample config file that comes with pdnsd, copy it + into your config file and forget about it. +
+
+ + + + + + + + + + +
Q: + When compiling, I get an error message like
Please define __BYTE_ORDER to + be __LITTLE_ENDIAN or __BIG_ENDIAN
What's up? +
A: + Normally, this macros should be defined in your C library's header files. + There are two different methods, most C libraries support both (and pdnsd + honors both): either __BYTE_ORDER is set to __LITTLE_ENDIAN + or __BIG_ENDIAN, or __LITTLE_ENDIAN or __BIG_ENDIAN + are directly defined as macros. +
+ Linux glibc, for example, does set those macros correctly. Never mind. You just have to know + whether your machine is little-endian or big-endian, this means wheter your + machine saves the least significant byte of a word or double-word first in memory (little-endian) or + the most significant first (big-endian). + All intel x86 and Alpha machines are little-endian, for example, while SPARC + and PowerPC architectures are big-endian. + If your machine is little-endian, add the following line to your config.h: +
+ #define __BYTE_ORDER __LITTLE_ENDIAN +
+ Likewise, if your machines byte order is big-endian: +
+ #define __BYTE_ORDER __BIG_ENDIAN +
+ Pathological byte orders like pdp-endian are not yet supported really; + However, for the place the endianess is needed, __LITTLE_ENDIAN should do + (it deals only with 16 bits; for all other occurances, ntoh[sl]/hton[sl] is used). +
+
+ + + + + + + + + + +
Q: + At startup, I get a warning saying:
+ + Uptest command [...] will implicitly be executed as root +
+ What does that mean? +
A: + This warning only occurs if you use the uptest=exec option in your + configuration. It means that the uptest command is run as root + because pdnsd is running as root, and this was not explicitely specified. + The idea is that it may introduce security holes (in the programs being run) + when they run as root, and so they shouldn't do that if possible. + You can specify the user that shall run the command by appending its name + comma-separated as string to the uptest_cmd line:
+ + uptest_cmd="<your command>","<user>"; +
+ If it is correctly running as root, just append the user string "root" to + the command and the warning will not occur again. +
+
+ + + + + + + + + + +
Q: + I cannot run my uptest_cmd command as root (it says permission denied), + although the pdnsd executable is setuid root. Why? +
A: + pdnsd will drop privileges gained through setuid/setgid before executing the + uptest commands (you shouldn't set the pdnsd executable setuid/setgid anyway). + The reason is clear: if you install the pdnsd + executable as setuid root and this wouln't be done, any user could execute + shellcode with root privileges using that option! +
+
+ + + + + + + + + + +
Q: + At startup, I get an error saying:
+ + Bad config file permissions: the file must be only writeable by the user +
+ Why is that? +
A: + pdnsd has an option (uptest=exec) that allows the execution of arbitrary + shell code (for testing whether an interface is up). This must be + of course secured against unauthorized use. One of these + protection is the one that produces the error message: if you routinely run + pdnsd, e.g. at system startup, and your config file is editable for others, + someone could change it and insert shell code that is executed in the next + pdnsd run -- with your user privileges! To prevent this, pdnsd will exit if the config file is writeable + by others than the owner. + To get rid of this message, just do
+ + chmod go-w <filename> +
+ on your config + file (for the default file: chmod go-w /etc/pdnsd.conf). + You should also check that the ownership is set correct. +
+
+ + + + + + + + + + +
Q: + serve_aliases does not seem to work. +
A: + Some resolvers (e.g. of the glibc 2.1) seem sometimes not to look up unmodified names, but the names with + an entry of the search path already appended. Since pdnsd will serve short names with this + option anyway, you can delete the search an domain options from your /etc/resolv.conf. This is reported to + work in some cases. +
+
+ + + + + + + + + + +
Q: + Some queries for domains that have many records (e.g. www.gmx.de) fail mysteriously. +
A: + pdnsd versions prior to 1.1.0 had the tcp server thread disabled by default. Most resolvers + repeat their query using tcp when they receive a truncated answer (the answer is truncated + when it exceeds a length of 512 bytes). You need to recompile pdnsd with the option + --enable-tcp-server to fix this. +
+
+ + + + + + + + + + +
Q: + I am behind some kind of firewall. In the configuration file + I have only listed addresses of name servers on the local (ISP's) network, + but pdnsd is slow and DNS queries frequently time out. +
A: + In some cases pdnsd will not consider the answer of the local name server + authoritative enough, and will try to get answers from the name servers listed in the + authority section of the reply message. If pdnsd is behind a firewall that blocks the + UDP reply packets from remote name servers, pdnsd will wait in vain for a reply. + One solution is to set proxy_only=on + in the servers sections of the configuration file. + This will prevent pdnsd from querying name servers that are not listed in the configuration + file. + Another solution that can be tried is specifying + query_method=tcp_only + in the global section of the configuration file, because a firewall that blocks + UDP packets from outside might still allow outgoing TCP connections to port 53. +
+
+ + + + + + + + + + +
Q: + Is pdnsd vulnerable to DNS cache poisoning as described in + CERT vulnerability note VU#800113? +
A: + Short answer: Yes.
+ Somewhat longer answer: The problem is not so much that pdnsd's implementation is flawed + but rather that the DNS protocol currently being used is fundamentally flawed from + a security viewpoint. As long as a more secure protocol is not in place, + all that the developers of pdnsd can do is to try to tweak the current implementation + to make it as difficult as possible for an attacker to succeed.
+ From version 1.2.7 onwards, the default for the query_port_start option + is 1024, which means that the pdnsd resolver will randomly select source ports + in the range 1024-65535. (In previous versions the default was to let the kernel select + the source ports, which will often result in a more or less predictable sequence of ports.) + It also helps to use a good quality source of random numbers. On platforms where this is + supported, it is preferable to configure with --with-random-device=/dev/urandom. + There is still more that can be done to make pdnsd less vulnerable, but this remains + (as of this writing) a work in progress. +
+ Please note that pdnsd was designed for small (private) networks, and that it is generally + not recommended to let untrusted users access pdnsd. +
+ +
+
Thomas Moestl + and Paul Rombouts +
+
+

+ Last revised: 18 August 2008 by Paul Rombouts +

+ + + diff --git a/app/src/main/jni/pdnsd/doc/html/htmlsubst.pl b/app/src/main/jni/pdnsd/doc/html/htmlsubst.pl new file mode 100644 index 0000000..a9e3e9f --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/htmlsubst.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w + +# Primitive ad-hoc script for updating pdnsd html doc files. +# Written by Paul Rombouts. + +use strict; +use integer; +use POSIX qw(strftime); + +my %paramvals=(); + +while(@ARGV && $ARGV[0]=~/^([^=]*)=(.*)$/) { + my $param=$1; my $val=$2; + if($param =~ /^[[:alpha:]]\w*$/) { + $paramvals{$param}=$val; + } + else {warn "Warning: invalid parameter '$param' ignored.\n"} + shift @ARGV; +} + +sub sizeof { + my($arg)=@_; + (my $str= $arg) =~ s/\$(?:([[:alpha:]]\w*)\b|\{([[:alpha:]]\w*)\})/ + defined($paramvals{$+})?$paramvals{$+}:defined($ENV{$+})?$ENV{$+}:''/eg; + my $filename=eval($str); + (-f $filename) or return '???'; + (((-s $filename)+1023)/1024).'kB'; +} + +while(<>) { + s/\$(?:date\b|\{date\})/strftime("%d %b %Y",localtime)/eg; + s/\$sizeof\(([^()]*)\)/sizeof($1)/eg; + s/\$(?:([[:alpha:]]\w*)\b|\{([[:alpha:]]\w*)\})/ + defined($paramvals{$+})?$paramvals{$+}:defined($ENV{$+})?$ENV{$+}:''/eg; + print; +} diff --git a/app/src/main/jni/pdnsd/doc/html/index.html b/app/src/main/jni/pdnsd/doc/html/index.html new file mode 100644 index 0000000..d2e426a --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html/index.html @@ -0,0 +1,686 @@ + + + + pdnsd homepage + + + + + + + + + + + + + + + +
+ About pdnsd + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+

The pdnsd Homepage

+

News

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2012-03-17Version 1.2.9a-par has been released. + Version 1.2.9a fixes a bug in the 1.2.9 release that causes a build failure when pdnsd is + configured with --enable-strict-rfc2181. + If you do not use this option to compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. +
2012-02-27Version 1.2.9-par has been released. + Version 1.2.9 supports many more RR types (including those necessary for DNSSEC) and + EDNS (Extension mechanisms for DNS) to enable UDP messages larger than 512 bytes. + It also has support for defining local TXT records and has several new options and bugfixes + (including file descriptor leaks that effect FreeBSD users). +
2011-05-03The latest source code is available from a + git repository.
+ In response to frequent requests I have uploaded a git tree including the latest code + and a fairly extensive history of pdnsd development to + gitorious.org. + Anyone who wants to participate in pdnsd development is free to create a + clone repo on gitorious.org + and push his modifications there. +
2010-02-22Version 1.2.8-par has been released. + The main new feature of version 1.2.8 is automatic discovery of root servers. + Furthermore, there are some additional improvements in the resolver. +
2008-09-04Version 1.2.7-par has been released. + Foremost, this release fixes some security problems. + It contains a fix for a "dangling pointer" bug that could cause pdnsd to + crash when it received a long reply. It also addresses some of the issues + raised in the CERT + vulnerability note VU#800113 by making the default of + query_port_start equal to 1024, thereby ensuring that source + ports are randomly selected by the pdnsd resolver in the range 1024-65535. + This release also fixes problems with compiling pdnsd for the ARM architecture + and for the Darwin platform (Max OS X). +
+ There are a number of (minor) new features. + pdnsd now supports "include" files, essentially configuration files that + only contain definitions for local records. + It is now possible to define interactively, using pdnsd-ctl, + any local record that can be defined in a configuration file. +
2007-09-04Version 1.2.6-par has been released. + pdnsd's license has been upgraded to GPL version 3. + A bug has been fixed which which caused pdnsd to handle NXDOMAIN replies + inefficiently when configured with neg_domain_pol=on. The + code that implements the ping test has been fixed, which was broken for + 64-bit systems. A new option randomize_servers can be used + to give each server in a section of the configuration file an equal + chance of being queried. The new options reject, + reject_policy and reject_recursively make it + possible to check for the presence of certain IP addresses in the + replies of name servers and to avoid some types of unwanted replies. + The pdnsd-ctl 'add a' and 'add aaaa' commands + now allow multiple IP addresses to be specified for the same name. + pdnsd's ability to resolve from root servers has been improved. +
2006-09-02Version 1.2.5-par has been released. + This release introduces a new query method: udp_tcp. + With this method a UDP query is tried first and, if the UDP answer is + truncated, the query is repeated using TCP, which is the behaviour that + seems to be recommended by the DNS standards. There is a new + configuration option use_nss, which can be turned off to + prevent lengthy timeouts and stalls in certain situations. A bug has + been fixed which could cause pdnsd to crash if debug output was + generated before the debug output stream was properly initialized. +
2006-01-09Version 1.2.4-par has been released. + A memory leak and a minor buffer-overflow problem have been fixed. + There is now a fix for some situations that would previously cause pdnsd to + exit prematurely (such as ACPI S3 sleep or trying to attach strace to pdnsd). + Time intervals specified in the configuration file can now be expressed in + minutes, hours, days and weeks as well as seconds. + Support for Apple Mac OS X v10.4 Tiger has been improved. + The "pdnsd-ctl status" command now also provides some + information about the status of the running threads. + There are some further improvements in the debugging information provided by pdnsd.
+ TCP-query support is now compiled in by default (but can still be disabled using + the configure option --disable-tcp-queries). +
2005-07-11Version 1.2.3-par has been released. + New feature in this release: the "pdnsd-ctl empty-cache" command can + be provided with an include/exclude list, allowing the user to specify a + selection of names to be removed, instead of emptying the cache completely.
+ Additional improvements: pdnsd should now remain responsive while executing the + "pdnsd-ctl empty-cache" command. + With the query_method=tcp_udp option pdnsd will now also + try a UDP query after a TCP connection times out, which should allow + pdnsd to resolve the same names with query_method=tcp_udp + as with query_method=udp_only, although perhaps with an + occasional delay. + "pdnsd-ctl config" or "pdnsd-ctl server" + commands should now run without delays, even if pdnsd is performing + ping or query uptests at the time. + Some problems with resolving certain names using root servers have been fixed. +
2005-04-03Version 1.2.2-par has been released. + The main emphasis of this release is improved portability. + A bug has been fixed that prevented pdnsd from compiling successfully on some + 64 bit architectures. + This release has (experimental) support for the Darwin (Apple Mac OS X) platform. + On Linux systems, the configure script will now try to detect automatically whether + the system implements the Native POSIX Thread Library, but the method used may not + necessarily be foolproof. + In addition, the debug features have been improved and should make it easier to find out + why pdnsd considers some queries or replies malformed. +
2004-11-07Version 1.2.1-par has been released. + The main new feature of this release is improved support for non-Linux platforms.
+ This release has (experimental) support for the Cygwin platform, and should also fix + some compilation glitches that have been reported by FreeBSD users. +
2004-10-10Version 1.2-par has been released. + pdnsd is new and improved! Most of the changes effect the internal workings + of pdnsd, but there also a number of interesting new features (well, I think they are interesting).
+ Among the bugs fixed are two rather nasty ones which involve the handling of NXT and NAPTR records + and which can cause pdnsd to crash or abort.
+ The new features include a new server availability test which can be specified with uptest=query, + support for reading the DNS configuration from resolv.conf files, + a new option for optimizing the use of root servers, + a new option that makes defining local records for reverse resolving easier, + support for defining wildcard records, + a new pdnsd-ctl command for reloading the config file without restarting pdnsd, and + a new pdnsd-ctl command for dumping information about the names stored in the cache. + The documentation has also been updated: there is now a pdnsd.conf man page.
+ For a more complete list of the changes I'll have to refer you to README.par and the ChangeLog. +
2004-05-22Version 1.1.11a-par has been released. + This release contains a fix for FreeBSD users that bypasses a problem + with the macro ENONET, which can cause a compilation failure when it is undefined. + Linux users will notice no difference between 1.1.11a-par and 1.1.11-par. +
2004-05-10Version 1.1.11-par has been released. + This version has a rather large number of small changes, which are rather difficult to summarize. + Among the bugs fixed are a race condition in the cache lookup code, a + flaw in the code that caused a busy spin when a remote server answered + with "Not Implemented", and problems with the -4 and -6 command-line + options. Among the improvements are an alternative sorting algorithm + which should allow pdnsd to start up faster when reading a large cache + file from disk, automatic mapping of IPv4 to IPv6 addresses when running + in IPv6 mode, somewhat more efficient memory use, better compression of + the replies and changes in the parallel querying algorithm that should + improve the chances of catching a reply from a remote server.
+ + For a more complete list of the changes I'll have to refer you to README.par and the ChangeLog. +
2004-02-10Version 1.1.10-par has been released. + + The main new feature of this release is a new parser for configuration + files, completely rewritten from scratch in C. The main advantages are: + (f)lex and yacc/bison are no longer needed to build pdnsd, more + informative error messages instead of merely "parse error", + and string literals no longer need to be enclosed in quotes in most + cases.
Furthermore, a bug has been fixed that caused incorrect + IPV6-type PTR records to be generated when sourcing + /etc/hosts like files.
+ + There have been other small changes, more details can be found in the ChangeLog. +
2004-01-08Version 1.1.9-par has been released. + "maintenance" release by Paul Rombouts.
+ + The change of version number is not very significant; the + difference between 1.1.9-par and the previous 1.1.8b1-par8 is marginal. + However, I felt the need to simplify the numbering, because it was + becoming rather baroque.
+ + I've added some missing pieces to the documentation (the pdnsd manual and the man page for pdnsd-ctl). BTW, did you + know that it's possible to define aliases for domain names with pdnsd? I + had plans to implement such a feature when I discovered that pdnsd + already supports it. It was just poorly documented. (If want to try this + for your self, look for the new information about CNAME records under + the rr Section in the manual.)
The + changes to the code consist mostly of optimizations, removal of some + size limits due to fixed-size buffers, and some cleaning up. I've also + tried to make the error responses of pdnsd-ctl more helpful.
+ + More details can be found in the ChangeLog. +
2003-10-10Version 1.1.8b1-par8 has been released. + "maintenance" release by Paul Rombouts.
+ This version introduces a "delegation-only" feature that may be useful + for blocking Verisign's Sitefinder.
+ The parser for the configuration file now tolerates domain names missing + a dot at the end.
+ I have provided alternative implementations for some GNU extensions that I + used in an effort to make the code more portable. In particular, the + code should build on FreeBSD again.
+ More details can be found in the README.par file. +
2003-09-19Version 1.1.8b1-par7 has been released. + "maintenance" release by Paul Rombouts. Besides fixing a number of bugs I have + reworked some of the code for adding and removing entries in the cache in an + effort to improve efficiency and stability.
+ More details can be found in the ChangeLog. +
2003-07-28Version 1.1.8b1-par6 has been released. + "maintenance" release by Paul Rombouts. In addition to some further code cleanup, + the documentation has been revised. +
2003-07-10Version 1.1.8b1-par5 has been released. + A troublesome allocation size error has been discovered in Thomas Moestl's code. + In practice this bug only wastes memory but it could + also potentially lead to memory corruption. Upgrading is recommended. + More details can be found in the ChangeLog. +
2003-06-30Version 1.1.8b1-par4 has been released. + Due to incompatibilities between various implementations of + the pthread library on Linux systems, problems can occur with signal handling in + pdnsd. The usual symptom is failure by pdnsd to save the cache to disk, and + /var/cache/pdnsd/pdnsd.cache remaining empty. If you experience + this kind of trouble, try reconfiguring with different values for the new + --with-thread-lib option. The allowable values are + described in the documentation. +
2003-04-07pdnsd is no longer maintained by Thomas Moestl: + I have not had time to maintain pdnsd for quite a while now, and have been very slow to + respond to issues, or did not respond at all. It is time that I officially announce that + pdnsd is no longer actively maintained; I apologize to all those who reported bugs or + asked questions without receiving any reply. However, Paul A. Rombouts has published + a patch set against the last released version at + http://members.home.nl/p.a.rombouts/pdnsd.html, + which cleans up a lot of code fixes many bugs. +
2002-07-19Documentation update. + Please note that pdnsd should never be installed with setuid or setgid attributes, + as it is not always possible to give up all privileges due to operating system restrictions. + While this was never intended and I don't think that anybody would actually do this, the + documentation was updated to explicitely mention this to avoid misunderstandings. +
2002-01-15Version 1.1.7a has been released. + This fixes a reversed test in an assertion that would cause pdnsd to termintate when the ping uptest + was used. No other changes were made. +
2002-01-15Version 1.1.7 has been released. + This fixes some problems that might be remotely exploitable to gain access as the user pdnsd runs as + (an unprivileged user by default). To do this, an attacker needs to control a name server that is + queried by pdnsd, and send a malicious reply to such a query.
+ Upgrading is strongly recommended!
+ There are also minor bug fixes and stability improvements. +
+ +
+

About pdnsd

+ pdnsd is a proxy DNS server with permanent caching (the cache contents + are written to hard disk on exit) that is designed to cope with unreachable + or down DNS servers (for example in dial-in networking).
+ Since version 1.1.0, pdnsd supports negative caching.
+
+ It is licensed under the GNU General Public License (GPL, + also available in html and + translated into various languages.). + This, in short, means that the sources are distributed togehter with the program, and + that you are free to modify the sources and redistribute them as long as you + also license them under the GPL. You do not need to pay anything for pdnsd. + It also means that there is ABSOLUTELY NO WARRANTY for pdnsd or any part + of it. For details, please read the GPL. +

+ pdnsd can be used with applications that do DNS lookups, e.g. on startup, and + can't be configured to change that behaviour, to prevent the often minute-long + hangs (or even crashes) that result from stalled DNS queries. Some Netscape Navigator + versions for Unix, for example, expose this behaviour. +

+ pdnsd is configurable via a file and supports run-time configuration using the program pdnsd-ctl that comes + with pdnsd. This allows you to set the status flags of servers that pdnsd knows (to influence which servers + pdnsd will query), and the addition, deletion and invalidation of DNS records in pdnsd's cache. +
+ Parallel name server queries are supported. This is a technique that allows + querying several servers at the same time so that very slow or unavailable + servers will not block the answer for one timeout interval. +
+ Since version 1.0.0, pdnsd has full IPv6 support. +

+ There is also a limited support for local zone records, intended for defining + 1.0.0.127.in-addr.arpa. and localhost. , since some clients request that + information and it must be served even if the cached servers are not available + or do not serve these records. pdnsd may also read your /etc/hosts file + (this file is normally used by your local resolver and usually contains + information for localhost as well as for your machines FQDN) and serve its + contents. +

+ pdnsd was started on Linux, and has since been ported to FreeBSD (and Cygwin and Darwin). + 90% of the source code should be easily portable to POSIX- + and BSD-compatible systems, provided that those systems support the POSIX threads (pthreads). + The rest might need OS-specific rewrites. +

+ Currently, pdnsd is only compileable by gcc. This should be easy to fix, but I just + do not have documentation for other compilers. If you are not able or do not want + to use gcc, I would recommend you just try to do the minor changes. +

+

+ pdnsd must be started as root in some cases (raw sockets are needed for icmp + echoes for the option uptest=ping, and the default port is 53, this must be + >1024 to allow non-root execution). However, pdnsd can be configured to change it's user + and group id to those of a non-privileged user after opening the sockets needed for this. +

+ The server should support the full standard DNS queries following the rfcs 1034 + and 1035. As of version 1.0.0, the rfc compliance has been improved again, and pdnsd is now + believed (or hoped?) to be fully rfc-compatible. It completely follows rfc 2181 (except + for one minor issue in the FreeBSD port, see the documentation). + It does not support the + following features, of which most are marked optional, experimental or obsolete + in these rfcs: +

+
    +
  • Inverse queries +
  • Status queries +
  • Completion queries +
  • Namespaces other than IN (Internet) +
  • AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain zones, that should not violate the standard +
+ The following record types, that are extensions to the original DNS standard, are supported for caching since version 1.2.9 + (if you do not need most of them, you can disable runtime support for the unneeded ones before compiling pdnsd and save a little cache and executable space, see the source file src/rr_types.in): +
    +
  • RP (responsible person, RFC 1183) +
  • AFSDB (AFS database location, RFC 1183) +
  • X25 (X25 address, RFC 1183) +
  • ISDN (ISDN number/address, RFC 1183) +
  • RT (route through, RFC 1183) +
  • NSAP (Network Service Access Protocol address , RFC 1348) +
  • PX (X.400/RFC822 mapping information, RFC 1995) +
  • GPOS (geographic position, deprecated) +
  • AAAA (IPv6 address, RFC 1886) +
  • LOC (location, RFC 1876) +
  • EID (Nimrod EID) +
  • NIMLOC (Nimrod locator) +
  • SRV (service record, RFC 2782) +
  • ATMA (ATM address) +
  • NAPTR (URI mapping, RFC 2168) +
  • KX (key exchange, RFC 2230) +
  • CERT (Certificate record, RFC 4398) +
  • DS (Delegation Signer, RFC 4034) +
  • RRSIG (Resource Record Signature, RFC 4034) +
  • NSEC (Next Secure, RFC 4034) +
  • DNSKEY (record containing the public key for a zone, RFC 4034) +
  • NSEC3 (Next Secure version 3, RFC 5155) +
  • NSEC3PARAM (NSEC3 parameters, RFC 5155) +
+

+ Note: This list is incomplete. For the complete list see the source file src/rr_types.in. +

+ There are FreeBSD and OpenBSD ports available for pdnsd (ports/net/pdnsd for both). + Thanks go to Roman Shterenzon for the FreeBSD port Sebastian Stark for the OpenBSD one! + Thanks to Kiyo Kelvin Lee now also runs on the Cygwin platform! + Thanks goes to Rodney Brown for extending portability to the Darwin (Apple Mac OS X) platform! +

+ If you have questions left, you should take a look into the FAQ. +
+ Bugfixes, patches and compatability fixes for other OSs are very welcome! +

+

Features in detail

+

+ This section describes some of pdnsds features in detail. Most of the options are set + in the config file. For more information on the configuration file, see + the documenation page. +


+ +

Uptests

+ pdnsd provides several methods to test whether a remote DNS server should be regarded as available + (so that pdnsd can query it), in + addition to the obvious "none" test (the server is always regarded as available, + or availability is set on or off using the pdnsd-ctl utility). + These tests are: +
    +
  • ping: a given adress is ping'ed in a given interval. If it there is no response + or the host is unreachable, the server is seen to be not available (for those who don't know: + pinging is sending a certain Internet packet type to a host to which any standard-conformant + host is required to reply). +
  • if: a given network interface is tested whether it is existent, up and running. If + it is not, the server is regarded to be not available. This is especially useful for ppp and + similar interfaces. A special case test for Linux isdn (ippp*) interfaces is integrated, so that the uptests + should also work for these. +
  • dev: this is a variant of the if uptest for use with Linux dial-on-demand ppp interfaces. In addition + to performing an if-style interface uptest, it also tests whether a specified program (e.g. pppd) owns + a lock to a given (modem-) device. +
  • exec: a given shell command line is executed and the exit status of the whole command line (which + is normally the exit status of the last command) is evaluated. If it is not zero, the server is regarded + to be not available. This is a very flexible testing method with which it should be able to perform + virtually any needed test. +
  • query: New in version 1.2: + This works like the ping test, except it sends an (empty) DNS query to the remote server. + If the server sends a well-formed response back within the timeout period (except SERVFAIL), + it will be regarded as available. + This test is useful if a remote server does not respond to ICMP_ECHO requests at all, + which unfortunately is quite common these days. + In many cases this test will be a more reliable indicator of availability + than the ones mentioned above. +
+
+

Local Records ("Zones")

+ As mentioned above, there are only very basic local record types (ie the record types that you may use in record + declarations in your local configuration for records that pdnsd shall serve in addion to the cached ones). + They are organized roughly in zones but have not complete zone declarations, so I generally do not use the + term "zone" for them, but rather "local records". + These are the local record types pdnsd can understand: +
    +
  • SOA (information about the name server) +
  • A (domain-name-to-address mapping) +
  • PTR (pointer, used normally for address-to-domain-name mapping) +
  • NS (name server, generated automatically by pdnsd for any local record set) +
  • CNAME (canonical host name) +
  • MX (mail exchange for the domain) +
  • TXT (arbitrary text strings, often used for Sender Policy Framework) +
+ You can specify these records in the configuration file.
+ You may "source" a file in a format like that used in the /etc/hosts file, that means + that pdnsd reads this file, extracts addresses and domain names from it and automatically generates + A records for name to address mapping, PTR records for address to name mapping and NS records (name + server specifiation) for each entry in the file.
+ Records can also be changed dynamically at run time.
+ A script contributed by Marko Stolle makes pdnsd usable in a DHCP setup using this feature. +
+

System requirements

+ As mentioned, pdnsd currently runs under Linux, FreeBSD and Cygwin. + Other BSD flavours may or may not work (feedback is very welcome!). + The system and software requirements under Linux are: +
    +
  • Kernel version >2.2.0 +
  • glibc version >2.0.1 (aka libc6) with LinuxThreads (normally included) + or NPTL (Native Posix Thread Library, recommended).
    + Due to a bug, pdnsd 0.9.8 does not run with glibc2.1.1. This behaviour was + fixed in pdnsd 0.9.9. +
  • For IPv6: glibc>=2.1 +
+ The system requirements under FreeBSD are: +
    +
  • FreeBSD versions >=2.6 (prior ones may or may not work) +
  • For IPv6: FreeBSD >=4.0 is recommended (no idea if it runs on prior versions) +
+ + The common software requirements for all supported systems are: +
    +
  • GCC, preferably egcs-2.* or 3.* (other compilers are currently not supported; the needed patch for another compiler + should not be difficult, however) +
  • GNU or BSD make +
  • the standard commands install, grep, sed, awk, touch and which (along with the REALLY + standard ones mv, cp, ln, rm, pwd, test, echo, cat, mkdir, chown, chmod, tar). In + any standard Unix installation, this should be no problem. +
  • for hacking and building own packages, you might also need gzip, bzip2, perl and rpmbuild +
+
+

Download

+ If you want to download pdnsd, please visit the download page. +
+

Authors

+

+ pdnsd was originally written by Thomas Moestl, + but is no longer maintained by him. Paul A. Rombouts + has revised large portions of the code and has added a number of new features. + See README.par and the ChangeLog + in the source directory (or /usr/share/doc/pdnsd-<version> + if you have installed a binary package) for more details. + If you have questions about the recent modifications, you can find + the email address of the current maintainer + at the end of README.par. +

+

+ Daniel Smolik has contributed RedHat RPMs (the most recent RPMs are available here).
+ Torben Janssen contributed start scripts for Red Hat Linux.
+ Soenke J. Peters contributed patches and suggestions for Red Hat compatability.
+ Wolfgang Ocker has contributed the code and documentation for the server_ip option.
+ Markus Mohr contributed a Debian rc script.
+ Nikita V. Youschenko contributed extensions to the "if" uptest.
+ Lyonel Vincent extended the serve_aliases option to support an arbitrary number of aliases.
+ Sourav K. Mandal wrote the autoconf scripts and contributed many fixes and suggestions.
+ Stephan Boettcher contributed the SCHEME= option.
+ Ron Yorston contributed the uptest for Linux ppp dial-on-demand devices.
+ Alexandre Nunes fixed some bugs in the autoconf files.
+ Sverker Wiberg contributed fixes for IPv6.
+ Carsten Block contributed configure-able rc scripts.
+ Olaf Kirch contributed a security fix for the run_as code.
+ Paul Wagland contributed various patches for bind9-compatability and other issues.
+ Roman Shterenzon contributed patches and lots of helpful hints for FreeBSD compatability.
+ Bernd Leibing has contributed spec file fixes.
+ Michael Wiedmann has contributed the pdnsd-ctl.8 man page.
+ Marko Stolle has contributed the contrib/pdnsd_update.pl script that makes pdnsd usable in a DHCP setup.
+ P.J. Bostley has contributed patches to get pdnsd working on alpha properly.
+ Christian Engstler contributed patches for SuSE compatability.
+ Bjoern Fischer contributed code to make pdnsd leave the case of names in the cache unchanged.
+ Marko Stolle contributed the contrib/pdnsd_update.pl script that makes pdnsd usable in a DHCP setup.
+ Andrew M. Bishop contributed the support for the label server option and the pdnsd-ctl interface for using it.
+ Frank Elsner contributed rc script fixes.
+ Andreas Steinmetz contributed the code for query_port_start and query_port_end options.
+ Mahesh T. Pai contributed the pdnsd.8 man page.
+ Nikola Kotur contributed the Slackware start-up script.
+ Kiyo Kelvin Lee contributed a patch for Cygwin support.
+ Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support.
+ Jan-Marek Glogowski contributed a patch implementing the use_nss option. +

+

+ Special thanks to Bert Frederiks for letting me do a late-night debugging run on his machine to + spot obscure bugs! +

+

+ Thanks to the following persons for reporting bugs and being helpful:
+ David G. Andersen,
+ Dirk Armbrust,
+ Daniel Black,
+ Kevin A. Burton,
+ Juliusz Chroboczek,
+ Joachim Dorner,
+ Stefan Erhardt,
+ Stefan Förster,
+ Mike Hammer,
+ Jonathan Hudson,
+ Dan Jacobson,
+ Byrial Jensen,
+ Patrick Loschmidt,
+ James MacLean,
+ Fraser McCrossan,
+ Michael Müller,
+ Erich Reitz,
+ Brian Schroeder,
+ Milan P. Stanic,
+ Michael Steiner,
+ Norbert Steinl,
+ Markus Storm,
+ Michael Ströder,
+ Alan Swanson,
+ Eelco Vriezekolk. +

+ +

Links

+ Well, this is the obligatory link section. +
+
+ + + + + + + + +
+ + http://www.gnu.org
+ The GNU homepage +
+
+ + http://freecode.com
+ Freecode (formerly Freshmeat) - large free software index +
+
+ + http://www.freebsd.org
+ The FreeBSD project +
+
+ +
+
+
Thomas Moestl + and Paul A. Rombouts +
+
+

+ Last revised: 17 March 2012 by Paul A. Rombouts +

+ + diff --git a/app/src/main/jni/pdnsd/doc/html2confman.pl b/app/src/main/jni/pdnsd/doc/html2confman.pl new file mode 100644 index 0000000..abade11 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/html2confman.pl @@ -0,0 +1,161 @@ +#!/usr/bin/perl -w +# +# A Perl script for converting pdnsd html documentation to a man page. +# +# Written by Paul A. Rombouts +# +# This file Copyright 2004 Paul A. Rombouts +# It may be distributed under the GNU Public License, version 2, or +# any higher version. See section COPYING of the GNU Public license +# for conditions under which this file may be redistributed. +# + +use strict; +use POSIX qw(strftime); + + +while(<>) { + if(/[^<]*configuration file/i) { + last; + } +} + +exit unless defined($_); + +while(<>) { + if(/[^<]*layout/i) { + last; + } +} + +exit unless defined($_); + +(my $myname=$0) =~ s{.*/}{}; + +print <) { + if(/.*\bpdnsd-ctl\b/) { + last; + } + s{^\s*((?:<[^<>]+>)*?)[\d.]*\s*(.*)((?:<[^<>]+>)*?)(?:
)?\s*$}{.SS $1$2$3\n}i; + if(s{^\s*\s*}{.TP\n}i) {$taggedparagraph=1} + if(m{^\s*}i) {$taggedparagraph=0} + s{^\s*((?:<[^<>]+>)*?)(.*)((?:<[^<>]+>)*?)(?:
)?\s*$}{.B $1$2$3\n}i if $taggedparagraph; + s{^\s*((?:<[^<>]+>)*?or(?:<[^<>]+>)*?)(?:
)?\s*$}{$1\n.PD 0\n.TP\n.PD\n}i if $taggedparagraph; + if(s{^\s*
}{.DS L\n}i) {$displayed=1}
+    s{^\t}{        } if $displayed;
+    if(s{
\s*$}{\n.DE\n\n}i) {$displayed=0} + elsif(!$displayed) {s{^\s*}{}} + s{^\s*
  • }{.IP\n\\(bu }i; + s{
  • }{\n.IP\n\\(bu }i; + s{
      }{\n}i; + s{
    }{\n}i; + s{}{\\fB}ig; + s{}{\\fP}ig; + s{<(i|em)>}{\\fI}ig; + s{}{\\fP}ig; + unless(s{^\s*(<[^<>]+>)*(
    |

    )(<[^<>]+>)*\s*$}{\n}i) { + s{]*>(.*)

    }{\n$1\n}i; + s{^\s*
    }{.br\n}i; + s{
    \s*
    \s*$}{\n\n}i; + s{
    \s*$}{\n.br\n}i; + s{
    }{\n.br\n}i; + s{^\s*(<[^<>]+>)*\s*$}{}; + } + s{<[^<>]+>}{}g; + s{<}{<}ig; + s{>}{>}ig; + s{"}{"}ig; + s{ }{\\ }ig; + s{/var/cache/pdnsd\b}{\@cachedir\@}g; + s{(? +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\\-par and later). +.PP +Several others have contributed to \\fBpdnsd\\fP; see files in the source or +\\fB/usr/share/doc/pdnsd/\\fP directory. +.PP +This man page was automatically generated from the html documentation for \\fBpdnsd\\fP, +using a customized Perl script written by Paul A. Rombouts. +ENDOFTRAILER + +if(defined($_)) { + while(<>) { + if(/last\s+revised/i) { + s{^\s*}{}; + s{<[^<>]+>}{}g; + s{<}{<}ig; + s{>}{>}ig; + s{"}{"}ig; + s{ }{\\ }ig; + print ".PP\n"; + print; + last; + } + } +} +exit; diff --git a/app/src/main/jni/pdnsd/doc/pdnsd-ctl.8 b/app/src/main/jni/pdnsd/doc/pdnsd-ctl.8 new file mode 100644 index 0000000..73459f3 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/pdnsd-ctl.8 @@ -0,0 +1,198 @@ +.\" This manpage has been automatically generated by docbook2man-spec +.\" from a DocBook document. docbook2man-spec can be found at: +.\" +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng . +.\" This manpage has been edited manually by Paul A. Rombouts. +.TH "PDNSD\-CTL" "8" "Sep 2008" "pdnsd 1.2.9b-par" "" +.SH NAME +\fBpdnsd\-ctl\fP \- controls pdnsd +.SH SYNOPSIS +.sp +\fBpdnsd\-ctl\fP [\fB\-c\fP \fIcachedir\fP] [\fB\-q\fP] \fIcommand\fP [\fIarguments\fP] +.SH "DESCRIPTION" +.PP +\fBpdnsd\-ctl\fP controls \fBpdnsd\fP, a proxy dns server with permanent caching. +Note that the status control socket must be enabled (by specifying an option on +the pdnsd command line or in the configuration file) before you can use +\fBpdnsd\-ctl\fP. +.PP +.TP +\fB\-c\fP \fIcachedir\fP +Set the cache directory to \fIcachedir\fP (must match pdnsd setting). +This is only necessary if the directory differs from the default specified +at compile time. +.TP +\fB\-q\fP +Be quiet unless output is specified by the command or something goes wrong. +.SH "COMMANDS" +.TP +\fBhelp\fP\ \ \ [no arguments] + +Print a command summary. +.TP +\fBversion\fP\ [no arguments] + +Print version and license info. +.TP +\fBstatus\fP\ [no arguments] + +Print a description of pdnsd's cache status, thread status and configuration. +Also shows which remote name servers are assumed to be available. +.TP +\fBserver\fP\ (\fIindex\fP|\fIlabel\fP) (\fBup\fP|\fBdown\fP|\fBretest\fP) [\fIdns1\fP[,\fIdns2\fP[,...]]] + +Set the status of the servers with the given index or label to up or down, or +force a retest. The index is assigned in the order of definition in pdnsd.conf +starting with 0. Use the status command to view the indexes. You can specify all +instead of an index to perform the action for all servers registered with pdnsd. +.IP +An optional third argument can be given consisting of a list of IP addresses +separated by commas or white-space characters. This list will replace the +addresses of name servers used by pdnsd for the given server section. This +feature is useful for run-time configuration of pdnsd with dynamic DNS data in +scripts called by ppp or DHCP clients. The last argument may also be an empty +string, which causes existing IP addresses to be removed and the corresponding +server section to become inactive. +.TP +\fBrecord\fP\ \fIname\fP (\fBdelete\fP|\fBinvalidate\fP) + +Delete or invalidate the records of the given domain name if it is in the cache. +Invalidation means that the records are marked as timed out, and will be +reloaded if possible. For local records (i.e., records that were given in the +config file using a rr section, records read from a hosts-style file and records +added using pdnsd-ctl), invalidation has no effect. Deletion will work, though. +.TP +\fBsource\fP\ \fIfn\fP \fIowner\fP [\fIttl\fP] [(\fBon\fP|\fBoff\fP)] [\fBnoauth\fP] + +Load a hosts-style file. Works like using the pdnsd source configuration section. +Owner and ttl are used as in the source section. ttl has a default +of 900 (it does not need to be specified). The next to last argument corresponds +to the serve_aliases option, and is off by default. +\fBnoauth\fP is used to make the domains non-authoritative +(this is similar to setting authrec=off in the config file, +please consult the +.BR pdnsd.conf (5) +man page for what that means). +fn is the name of the file, which must be readable by pdnsd. +.TP +\fBadd\fP\ \ \ \ \fBa\fP \fIaddr\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBaaaa\fP \fIaddr\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBptr\fP \fIhost\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBcname\fP \fIhost\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBmx\fP \fIhost\fP \fIname\fP \fIpref\fP [\fIttl\fP] [\fBnoauth\fP] + +Add a record of the given type to the pdnsd cache, replacing existing +records for the same name and type. The 2nd argument corresponds +to the value of the option in the rr section that is named like +the first argument. The addr argument may be a list of IP addresses, +separated by commas or white space. +The ttl is optional, the default is 900 seconds. +\fBnoauth\fP is used to make the domains non-authoritative +(this is similar to setting authrec=off in the config file, +please consult the +.BR pdnsd.conf (5) +man page for what that means). +If you want no other record than the newly added in the cache, do +\fBpdnsd\-ctl\fP\ \fBrecord\fP\ \fIname\fP\ \fBdelete\fP +before adding records. +.TP +\fBneg\fP\ \ \ \ \fIname\fP [\fItype\fP] [\fIttl\fP] + +Add a negatively cached record to pdnsd's cache, replacing existing +records for the same name and type. If no type is given, the whole +domain is cached negatively. For negatively cached records, errors are +immediately returned on a query, without querying other servers first. +The ttl is optional, the default is 900 seconds. +.TP +\fBconfig\fP\ \fIfilename\fP + +Reload pdnsd's configuration file. +.br +The config file must be owned by the uid that pdnsd had when it was started, +and be readable by pdnsd's run_as uid. +If no file name is specified, the config file used at start-up is reloaded. +Note that some configuration changes, like the port or IP address pdnsd listens on, +cannot be made this way and you will receive an error message. +In these cases, you will have to restart pdnsd instead. +.TP +\fBinclude\fP\ \fIfilename\fP + +Parse an include file. +.br +The include file may contain the same +type of sections as a config file, expect for global and server +sections, which are not allowed. This command can be used to add data +to the cache without reconfiguring pdnsd. +.TP +\fBeval\fP\ \ \ \fIstring\fP + +Parse a string as if part of an include file. +.br +The string should hold one or more complete configuration sections, +but no global and server sections, which are not allowed. +If multiple strings are given, they will be joined using newline chars +and parsed together. +.TP +\fBempty\-cache\fP\ [[+|-]\fIname\fP ...] + +Delete all entries in the cache matching include/exclude rules. +.br +If no arguments are provided, the cache is completely emptied, +freeing all existing entries. +Note that this also removes local records, as defined by the config file. +To restore local records, run "pdnsd-ctl\ config" immediately afterwards. +.br +If one or more arguments are provided, these are interpreted as +include/exclude names. If an argument starts with a '+' the name is to +be included. If an argument starts with a '-' it is to be excluded. +If an argument does not begin with '+' or '-', a '+' is assumed. +If the domain name of a cache entry ends in one of the names in the +list, the first match will determine what happens. If the matching name +is to be included, the cache entry is deleted, otherwise it remains. +If there are no matches, the default action is not to delete. +.TP +\fBdump\fP\ \ \ [\fIname\fP] + +Print information stored in the cache about \fIname\fP. +If \fIname\fP begins with a dot and is not the root domain, information +about the names in the cache ending in \fIname\fP (including \fIname\fP without +the leading dot) will be printed. +If \fIname\fP is not specified, information about all the names in the cache +will be printed. +.TP +\fBlist\-rrtypes\fP [no arguments] + +List available rr types for the neg command. Note that those are only +used for the neg command, not for add! +.SH "BUGS" +.PP +If you pipe the output of \fBdump\fP command through an application that +reads only part of the output and then blocks (such as more or less), +pdnsd threads trying to add new entries to the cache will be suspended +until the pipe is closed. +It is preferable to capture the output in a file in such a case. +.br +Report any remaining bugs to the authors. +.SH "AUTHORS" +.PP +Thomas Moestl +.UR + +.UE +.br +Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later) +.PP +Last revised: 04 Sep 2008 by Paul A. Rombouts. +.SH "SEE ALSO" +.PP +.BR pdnsd (8), +.BR pdnsd.conf (5) diff --git a/app/src/main/jni/pdnsd/doc/pdnsd.8.in b/app/src/main/jni/pdnsd/doc/pdnsd.8.in new file mode 100644 index 0000000..ba4330a --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/pdnsd.8.in @@ -0,0 +1,326 @@ +.TH PDNSD 8 "Jul 2007" "pdnsd @fullversion@" "System Administration Commands" + +.SH NAME +\fBpdnsd\fP \- dns proxy daemon + +.SH SYNOPSIS + +\fBpdnsd\fP [\-h] [\-V] [\-s] [\-d] [\-g] [\-t] [\-p \fIfile\fR] [\-v\fIn\fR] [\-m\fIxx\fR] [\-c \fIfile\fR] [\-4] [\-6] [\-a] +.PP +This man page is an extract of the documentation of \fBpdnsd\fP. +For complete, current documentation, refer to the HTML (or plain text) +documentation (which you can find in the \fBdoc/\fP subdirectory of the +source or in a standard documentation directory, typically +\fB/usr/share/doc/pdnsd/\fP if you are using a binary package). + +.SH DESCRIPTION +.PP +\fBpdnsd\fP is a IPv6 capable proxy domain name server (DNS) which +saves the contents of its DNS cache to the disk on exit. + +.SH OPTIONS + +.RS +.TP +.B \-4 +enables IPv4 support. IPv6 support is automatically +disabled (should it be available). On by default. +.TP +.B \-6 +enables IPv6 support. IPv4 support is automatically +disabled (should it be available). Off by default. +.TP +.B \-a +With this option, pdnsd will try to detect automatically if +the system supports IPv6, and fall back to IPv4 otherwise. +.TP +.BR \-V " or " \-\-version +Print version information and exit. +.TP +\fB\-c\fP \fIFILE\fP or \fB\-\-config\-file=\fP\fIFILE\fP +specifies that configuration is to be read from \fIFILE\fP. +Default is \fB@sysconfdir@/pdnsd.conf\fP. +.TP +.BR \-d " or " \-\-daemon +Start \fBpdnsd\fP in daemon mode (as a background process). +.TP +.BR \-g " or " \-\-debug +Print some debug messages on the console or to the file +\fBpdnsd.debug\fP in your cache directory (in daemon mode). +.TP +.BR \-h " or " \-\-help +Print an option summary and exit. +.TP +\fB\-i\fP \fIPREFIX\fP or \fB\-\-ipv4_6_prefix=\fP\fIPREFIX\fP +specifies the prefix pdnsd uses (when running in IPv6 mode) to map IPv4 +addresses in the configuration file to IPv6 addresses. Must be a valid IPv6 +address. Default is ::ffff:0.0.0.0 +.TP +.B \-p \fIFILE\fP +writes the pid the server runs as to the specified filename. Works +only in daemon mode. +.TP +.B \-\-pdnsd\-user +Print the user \fBpdnsd\fP will run as and exit. +.TP +.BR \-s " or " \-\-status +enables the status control socket. Either this option should be passed +to the command line or \fBstatus_ctl=on;\fP should be specified in the +config file if you want to use +.BR pdnsd\-ctl (8) +to control \fBpdnsd\fP at runtime. +.TP +.BR \-t " or " \-\-tcp +enables the TCP server thread. \fBpdnsd\fP will then serve TCP and UDP +queries. +.TP +.BI \-v n +sets the verbosity of \fBpdnsd\fP. \fIn\fP is a numeric argument +between 0 (normal operation) to 3 (many messages for debugging). +.TP +.BI \-m xx +sets the query method \fBpdnsd\fP +uses. Possible values for \fIxx\fP are: +.IP +.B uo +\- pdnsd will use UDP only. This is the fastest method, and should +be supported by all name servers on the Internet. + +.IP +.B to +\- pdnsd will use TCP only. TCP queries usually take more time than +UDP queries, but are more secure against certain attacks, where an +attacker tries to guess your query id and to send forged answers. TCP +queries are not supported by some name servers. + +.IP +.B tu +\- pdnsd will try to use TCP, and will fall back to UDP if its +connection is refused or times out. + +.IP +.B ut +\- pdnsd will try to use UDP, and will repeat the query using TCP +if the UDP reply was truncated (i.e. the tc bit is set). +This is the behaviour recommended by the DNS standards. + +.PP +Additionally, "no" can be prepended to the \-\-status, \-\-daemon, \-\-debug +and \-\-tcp options (e.g. \-\-notcp) to reverse their effect. +.RE + +.SH USAGE +.PP +\fBpdnsd\fP is usually run from a startup script. For \fBpdnsd\fP to +work, You need to:- + +.IP +1. Tell your system to use \fBpdnsd\fP as the primary DNS server by +modifying \fB/etc/resolv.conf\fP. + +.IP +2. Tell \fBpdnsd\fP to use an authentic source for DNS records, by +including the IP addresses of one or more DNS servers, usually your +ISP's DNS servers, in \fB@sysconfdir@/pdnsd.conf\fP. +.PP +For this, put the following line in your \fB/etc/resolv.conf\fP +.PP +.RS +nameserver 127.0.0.X +.RE +.PP +where X can be any number. (I use 3). Comment out all other +entries. You should put the same value in the server_ip= line in +\fBglobal\fP section of \fB@sysconfdir@/pdnsd.conf\fP. +.br +If you want to use \fBpdnsd\fP as the DNS server for a small local network, +you should use the IP address or name of the interface connected to +this network instead of 127.0.0.X. +.RE + +.PP +To tell \fBpdnsd\fP where to get DNS information from, add the +following lines in \fB@sysconfdir@/pdnsd.conf\fP:- + +.PP +.RS +server { +.br + label= "myisp"; + ip=123.456.789.001,123.456.789.002; + proxy_only=on; + timeout=10; +.br +} +.RE +.PP +Note the opening and closing braces. Add more such \fBserver\fP +sections for each set of DNS servers you want \fBpdnsd\fP to query. +Of course the configuration options shown here are just examples. +More examples can be found in \fB@sysconfdir@/pdnsd.conf.sample\fP +or the pdnsd.conf in the documentation directory. +See the +.BR pdnsd.conf (5) +man page for all the possible options and their exact meaning. +.PP +If you use a dial up connection, remember that ppp scripts usually +replace \fB/etc/resolv.conf\fP when connection with the ISP is +established. You need to configure ppp (or whatever you use to +establish a connection) so that \fB/etc/resolv.conf\fP is not replaced +every time a connection is established. Read the documentation for the +scripts run when your network comes up. +.PP +If you use pppconfig, specify `none' in the `nameservers' option in +the `advanced' tab. If you use multiple ISPs, you should do this for +each connection/account. +.PP +If you use multiple ISPs, you should tell \fBpdnsd\fP which DNS servers +have become available by calling \fBpdnsd\-ctl\fP, the \fBpdnsd\fP +control utility, in a script (e.g. \fB/etc/ppp/ip\-up\fP when you use pppd) +that is run when the connection is established. +If the addresses of the DNS servers are obtained through some type of +dynamic configuration protocol (e.g. pppd with the usepeerdns +option or a DHCP client), you can pass the DNS server addresses as an extra +argument to \fBpdnsd\-ctl\fP to configure \fBpdnsd\fP at run time. +See the +.BR pdnsd\-ctl (8) +man page for details. + +.SH FILES + +\fB@sysconfdir@/pdnsd.conf\fP is the pdnsd configuration file. +The file format and configuration options are described in the +.BR pdnsd.conf (5) +man page. You can find examples of almost all options in +\fB@sysconfdir@/pdnsd.conf.sample\fP. +.PP +\fB@cachedir@/pdnsd.cache\fP +.PP +\fB@cachedir@/pdnsd.status\fP is the status control socket, which must be +enabled before you can use \fBpdnsd\-ctl\fP. +.PP +\fB/etc/init.d/pdnsd\fP (the name and location of the start-up script +may be different depending on your distribution.) +.PP +\fB/etc/resolv.conf\fP +.PP +\fB/etc/defaults/pdnsd\fP contains additional parameters or options +which may be passed to pdnsd at boot time. This saves the hassle of +fiddling with initscripts (not available on all distributions). + +.SH BUGS +.PP +The verbosity option +.BI -v n +presently does not seem to have much effect on the amount of debug output. +.br +Report any remaining bugs to the authors. + +.SH CONFORMING TO +.PP +\fBpdnsd\fP should comply with RFCs 1034 and 1035. As of version +1.0.0, RFC compliance has been improved and pdnsd is now believed (or +hoped?) to be fully RFC compatible. It completely follows RFC 2181 +(except for one minor issue in the FreeBSD port, see the +documentation). +.PP +It does \fINOT\fP support the following features, of which most are +marked optional, experimental or obsolete in these RFCs: + + +.IP +\(bu Inverse queries +.IP +\(bu Status queries +.IP +\(bu Completion queries +.IP +\(bu Namespaces other than IN (Internet) +.IP +\(bu AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain zones, that should not violate the standard + +.PP +The following record types, that are extensions to the original DNS +standard, are supported if given as options at compile time. (if you +do not need them, you do not need to compile support for them into +pdnsd and save cache and executable space): + +.IP +\(bu RP (responsible person, RFC 1183) +.IP +\(bu AFSDB (AFS database location, RFC 1183) +.IP +\(bu X25 (X25 address, RFC 1183) +.IP +\(bu ISDN (ISDN number/address, RFC 1183) +.IP +\(bu RT (route through, RFC 1183) +.IP +\(bu NSAP (Network Service Access Protocol address , RFC 1348) +.IP +\(bu PX (X.400/RFC822 mapping information, RFC 1995) +.IP +\(bu GPOS (geographic position, deprecated) +.IP +\(bu AAAA (IPv6 address, RFC 1886) +.IP +\(bu LOC (location, RFC 1876) +.IP +\(bu EID (Nimrod EID) +.IP +\(bu NIMLOC (Nimrod locator) +.IP +\(bu SRV (service record, RFC 2782) +.IP +\(bu ATMA (ATM address) +.IP +\(bu NAPTR (URI mapping, RFC 2168) +.IP +\(bu KX (key exchange, RFC 2230) + +.SH SEE ALSO +.PP +.BR pdnsd\-ctl (8), +.BR pdnsd.conf (5), +.BR pppconfig (8), +.BR resolv.conf (5) +.PP +More documentation is available in the \fBdoc/\fP subdirectory of the source, +or in \fB/usr/share/doc/pdnsd/\fP if you are using a binary package. + +.SH AUTHORS + +\fBpdnsd\fP was originally written by Thomas Moestl, +.UR +, +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later). +.PP +Several others have contributed to \fBpdnsd\fP; see files in the +source or \fB/usr/share/doc/pdnsd/\fP directory. +.PP +This man page was written by Mahesh T. Pai +.UR + +.UE +using the documents in \fB/usr/share/docs/pdnsd/\fP directory for Debian, +but can be used on other distributions too. +.PP +Last revised: 22 Jul 2007 by Paul A. Rombouts. + +.SH COPYRIGHT + +.PP +This man page is a part of the pdnsd package, and may be distributed +in original or modified form under terms of the GNU General Public +License, as published by the Free Software Foundation; either version +3, or (at your option) any later version. + +.PP +You can find a copy of the GNU GPL in the file \fBCOPYING\fP in the source +or the \fB/usr/share/common\-licenses/\fP directory if you are using a +Debian system. diff --git a/app/src/main/jni/pdnsd/doc/pdnsd.conf.5.in b/app/src/main/jni/pdnsd/doc/pdnsd.conf.5.in new file mode 100644 index 0000000..801b535 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/pdnsd.conf.5.in @@ -0,0 +1,1328 @@ +.\" Generated automatically from the html documentation by html2confman.pl +.\" +.\" Manpage for pdnsd.conf (pdnsd configuration file) +.\" +.\" Copyright (C) 2000, 2001 Thomas Moestl +.\" Copyright (C) 2003, 2004, 2005, 2006, 2007 Paul A. Rombouts +.\" +.\" This manual is a part of the pdnsd package, and may be distributed in +.\" original or modified form under terms of the GNU General Public +.\" License, as published by the Free Software Foundation; either version +.\" 3, or (at your option) any later version. +.\" You can find a copy of the GNU GPL in the file COPYING in the source +.\" or documentation directory. +.\" +.TH PDNSD.CONF 5 "Apr 2012" "pdnsd @fullversion@" +.SH NAME +pdnsd.conf \- The configuration file for pdnsd +.hw config +.SH DESCRIPTION +.PP +This manual page describes the layout of the +.BR pdnsd (8) +configuration file and the available configuration options. +The default location of the file is @sysconfdir@/pdnsd.conf. This may be changed +with the \fB-c\fP command line option. +An example pdnsd.conf comes with the pdnsd distribution in the documentation directory +or in @sysconfdir@/pdnsd.conf.sample. +.SH "FILE FORMAT" +.PP +The configuration file is divided into sections. Each section is prefixed with +the section name and opening curlies ({) and closed with closing curlies (}). +In each section, configuration options can be given in the form + +\fIoption_name\fP=\fIoption_value\fP; + +Option value may be a string literal, a number, a time specification or a constant. +In previous versions of pdnsd strings had to be enclosed +in quotes ("), but since version 1.1.10 this is no longer necessary, unless +a string contains a special character such as whitespace, a token that normally starts +a comment, or one of ",;{}\". +Since version 1.2.9 a backslash (\) inside a string is interpreted as an escape character, +so it is possible to include special characters in strings (both quoted or unquoted) +by preceding them with a backslash. Some escape sequences are in interpreted as in the C +programming language, e.g. \t becomes a tab, +\n becomes a new-line control char. +.br +A time specification consists a sequence of digits followed by a one-letter suffix. +The following suffixes are recognized: +s (seconds), m (minutes), h (hours), +d (days) and w (weeks). +If the suffix is missing, seconds are assumed. +If several time specifications are concatenated, their values are added together; +e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. +.br +Some options take more than one value; in this case, the values are separated with commas. +.br +If you may supply one of a set of possible values to an option, this is noted +in the documentation as +(option1|option2|option3|...) +.br +The constants true|false and yes|no +are accepted as synonyms for the constants on|off. +.br +Comments may be enclosed in /* and */, nested comments are possible. If the +# sign or two slashes (//) appear in the configuration file, everything from +these signs to the end of the current line is regarded as a comment and ignored. +.br +There are examples for nearly all options in the sample config file. + +.SS global Section +The global section specifies parameters that affect the overall behaviour of the +server. If you specify multiple global sections, the settings of those later in +the file will overwrite the earlier given values. +.br +These are the possible options: + +.TP +.B perm_cache=(\fInumber\fP|off); +Switch the disk cache off or supply a maximum cache size in kB. If the disk +cache is switched off, 8 bytes will still be written to disk. +The memory cache is always 10kB larger than the file cache. +This value is 2048 (2 MB) by default. +.TP +.B cache_dir=\fIstring\fP; +Set the directory you want to keep the cache in. +The default is "@cachedir@" +(unless pdnsd was compiled with a different default). +.TP +.B server_port=\fInumber\fP; +Set the server port. This is especially useful when you want to start the +server and are not root. Note that you may also not specify uptest=ping in +the server section as non-root. +.br +The default port is 53, the RFC-standard one. Note that you should only use +non-standard ports when you only need clients on your machine to communicate +with the server; others will probably fail if the try to contact the server +on the basis of an NS record, since the A record that supplies the address for +(among others) name servers does not have a port number specification. +.TP +.B server_ip=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B interface=\fIstring\fP; +Set the IP address pdnsd listens on for requests. This can be useful +when the host has several interfaces and you want pdnsd not to listen on +all interfaces. For example, it is possible to bind pdnsd to listen on +127.0.0.2 to allow pdnsd to be a forwarder for BIND. +The default setting for this option is server_ip=any, which means that +pdnsd will listen on all of your local interfaces. +Presently you can only specify one address here; if you want pdnsd to listen on multiple +interfaces but not all you will have to specify server_ip=any +and use firewall rules to restrict access. +.br +The IP address used to need quotation marks around it, but since version 1.1.10 +this is no longer necessary. +.br +If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to +specify an IPv6 address here, then unless pdnsd was compiled to start up in IPv6 mode +by default, you will need to use the \-6 command-line option or +set run_ipv4=off first (see below) in order to ensure that the +IPv6 address is parsed correctly. +.br +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, +it will automatically be mapped to an IPv6 address. +.br +\fINew in version 1.2:\fP You may also give the name of an interface +such as "lo" or "eth0" here, instead of an IP address +(this has been tested on Linux, and may or may not work on other platforms). +pdnsd will not bind to the interface name, but will look up the address of the +interface at start-up and listen on that address. If the address of the interface +changes while pdnsd is running, pdnsd will not notice that. You will need to +restart pdnsd in that case. +.TP +.B outgoing_ip=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B outside_interface=\fIstring\fP; +\fINew in version 1.2.9:\fP +Set the IP address of the interface used by pdnsd for outgoing queries. +This can be useful when the host has several interfaces and you want pdnsd +to send outgoing queries via only one of them. +For example, if pdnsd is running on a host with one interface with IP address +192.168.1.1 connected to the local network, and another with IP address 123.xxx.yyy.zzz +connected to the internet, you may specify server_ip=192.168.1.1 +and outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds +to queries received from the local network, and only sends outgoing queries via +the interface connected to the internet. +.br +The default setting for this option is any, which means that +the kernel is free to decide which interface to use. +Like with the server_ip option, you may also give the name of an +interface here, instead of an IP address. +.TP +.B linkdown_kluge=(on|off); +This option enables a kluge that some people might need: when all servers are +marked down, with this option set the cache is not even used when a query is +received, and a DNS error is returned in any case. The only exception from this +is that local records (as specified in rr and source +sections are still served normally. +In general, you probably want to get cached entries even when the network is down, +so this defaults to off. +.TP +.B max_ttl=\fItimespec\fP; +This option sets the maximum time a record is held in cache. All dns +resource records have a time to live field that says for what period of time the +record may be cached before it needs to be requeried. If this is more than the +value given with max_ttl, this time to live value is set to max_ttl. +This is done to prevent records from being cached an inappropriate long period of time, because +that is almost never a good thing to do. Default is 604800s (one week). +.TP +.B min_ttl=\fItimespec\fP; +This option sets the minimum time a record is held in cache. All dns +resource records have a time to live field that says for what period of time the +record may be cached before it needs to be requeried. If this is less than the +value given with min_ttl, this time to live value is set to min_ttl. +Default is 120 seconds. +.TP +.B neg_ttl=\fItimespec\fP; +This option sets the time that negatively cached records will remain valid in the +cache if no time to live can be determined. This is always the case when whole +domains are being cached negatively, and additionally when record types are cached +negatively for a domain for which no SOA record is known to pdnsd. If a SOA is present, +the ttl of the SOA is taken. +.TP +.B neg_rrs_pol=(on|off|auth|default); +This sets the RR set policy for negative caching; this tells pdnsd under which circumstances +it should cache a record type negatively for a certain domain. off will +turn the negative caching of record types off, on will always add a negative +cache entry when a name server did not return a record type we asked it for, and auth +will only add such entries if the answer came from an authoritative name server for that +domain. +.br +\fINew in version 1.2.8:\fP The default setting will add a negatively cached record +if either the answer was authoritive or the answer indicated the name server had "recursion available" +while the query explicitly requested such recursion. +.br +The preset is "default" (used to be auth). +.TP +.B neg_domain_pol=(on|off|auth); +This is analogue to neg_rrs_pol for whole domain negative caching. It should be safe +to set this on, because I have not seen a caching server that will falsely claim that a +domain does not exist. +.br +The default is auth. +.TP +.B run_as=\fIstring\fP; +This option allows you to let pdnsd change its user and group id after operations that needed +privileges have been done. This helps minimize security risks and is therefore recommended. The +supplied string gives a user name whose user id and primary group id are taken. +.br +A little more details: after reading the config file, becoming a daemon (if specified) and starting +the server status thread, the main thread changes its gid and uid, as do all newly created threads +thereafter. By taking another uid and gid, those threads run with the privileges of the +specified user. +Under Linux and FreeBSD, the server status thread runs with the original privileges only when the strict_setuid option +is set to off (see below, on by default), because these may be needed +for exec uptests. The manager thread also retains its original privileges in this case. +You should take care that the user you specify has write permissions on your cache file and +status pipe (if you need a status pipe). You should look out for error messages like "permission denied" +and "operation not permitted" to discover permission problems. +.br +.TP +.B strict_setuid=(on|off); +When used together with the run_as option, this option lets you specify that all threads of the +program will run with the privileges of the run_as user. This provides higher security than +the normal run_as +option, but is not always possible. See the run_as option for further discussion. +.br +This option is on by default. +.br +Note that this option has no effect on Non-Linux systems. +.TP +.B paranoid=(on|off); +Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers to query other servers themselves +if possible, +and to give back answers for domains that may not be in its authority), and accepts additional records with information +for servers that are not in the authority of the queried server. This opens the possibility of so-called cache poisoning: +a malicious attacker might set up a dns server that, when queried, returns forged additional records. This way, he might +replace trusted servers with his own ones by making your dns server return bad IP addresses. This option protects +you from cache poisoning by rejecting additional records +that do not describe domains in the queried servers authority space and not doing recursive queries any more. +An exception +to this rule are the servers you specify in your config file, which are trusted. +.br +The penalty is a possible performance decrease, in particular, more queries might be necessary for the same +operation. +.br +You should also notice that there may be other similar security problems, which are essentially problems of +the DNS, i.e. +any "traditional" server has them (the DNS security extensions solve these problems, but are not widely +supported). +One of this vulnerabilities is that an attacker may bombard you with forged answers in hopes that one may match a +query +you have done. If you have done such a query, one in 65536 forged packets will be succesful (i.e. an average packet +count of 32768 is needed for that attack). pdnsd can use TCP for queries, +which has a slightly higher overhead, but is much less vulnerable to such attacks on sane operating systems. Also, pdnsd +chooses random query ids, so that an attacker cannot take a shortcut. If the attacker is able to listen to your network +traffic, this attack is relatively easy, though. +.br +This vulnerability is not pdnsd's fault, and is possible using any conventional +name server (pdnsd is perhaps a little more secured against this type of attacks if you make it use TCP). +.br +The paranoid option is off by default. +.br +.TP +.B ignore_cd=(on|off); +\fINew in version 1.2.8:\fP This option lets you specify that the CD bit of a DNS query will be ignored. +Otherwise pdnsd will reply FORMERR to clients that set this bit in a query. +It is safe to enable this option, as the CD bit refers to 'Checking Disabled' +which means that the client will accept non-authenticated data. +.br +This option is on by default. Turn it off if you want the old behaviour (before version 1.2.8). +.TP +.B scheme_file=\fIstring\fP; +In addition to normal uptests, you may specify that some servers shall only be queried when a certain +pcmcia-cs scheme is active (only under linux). For that, pdnsd needs to know where the file resides that +holds the pcmcia scheme information. Normally, this is either /var/lib/pcmcia/scheme or +/var/state/pcmcia/scheme. +.TP +.B status_ctl=(on|off); +This has the same effect as the \-s command line option: the status control is enabled when +on is specified. +.br +\fIAdded by Paul Rombouts\fP: Note that pdnsd\-ctl allows run-time configuration of pdnsd, +even the IP addesses of the name servers can be changed. If you're not using pdnsd\-ctl and +you want maximum security, you should not enable this option. It is disabled by default. +.TP +.B daemon=(on|off); +This has the same effect as the \-d command line option: the daemon mode is enabled when +on is specified. +.br +Default is off. +.TP +.B tcp_server=(on|off); +tcp_server=on has the same effect as the \-t or \-\-tcp +command-line option: it enables TCP serving. +Similarly, tcp_server=off is like the \-\-notcp command-line option. +.br +Default is on. +.TP +.B pid_file=\fIstring\fP; +This has the same effect as the \-p command line option: you can specify a file that pdnsd +will write its pid into when it starts in daemon mode. +.TP +.B verbosity=\fInumber\fP; +This has the same effect as the \-v command line option: you can set the verbosity of pdnsd's +messages with it. The argument is a number between 0 (few messages) to 3 (most messages). +.TP +.B query_method=(tcp_only|udp_only|tcp_udp|udp_tcp); +This has the same effect as the \-m command line option. +Read the documentation for the command line option on this. +tcp_only corresponds to the to, udp_only to the uo, +tcp_udp to the tu and udp_tcp to the ut +argument of the command line option. +.br +If you use query_method=tcp_udp, it is recommended that you also set the global timeout option to at least twice the longest server timeout. +.TP +.B run_ipv4=(on|off); +This has the same effect as the \-4 or \-6 command line option: +if on is specified, IPv4 support is enabled, and IPv6 support is disabled (if available). +If off is specified, IPv4 will be disabled and IPv6 will be enabled. +For this option to be meaningful, pdnsd needs to be compiled with support for the protocol you choose. +If pdnsd was compiled with both IPv4 and IPv6 support, and you want to include IPv6 addresses +in the configuration file, you will probably need to specify run_ipv4=off first to +ensure that the IPv6 addresses are parsed correctly. +.TP +.B debug=(on|off); +This has the same effect as the \-g command line option: the debugging messages are enabled when +on is specified. +.TP +.B ctl_perms=\fInumber\fP; +This option allows you to set the file permissions that the pdnsd status control socket will have. These +are the same as file permissions. The owner of the file will be the run_as user, or, if none is specified, +the user who started pdnsd. If you want to specify the permissions in octal (as usual), don't forget +the leading zero (0600 instead of 600!). To use the status control, write access is needed. The default +is 0600 (only the owner may read or write). +.br +Please note that the socket is kept in the cache directory, and that the cache directory permissions +might also need to be adjusted. Please ensure that the cache directory is not writeable for untrusted +users. +.TP +.B proc_limit=\fInumber\fP; +With this option, you can set a limit on the pdnsd threads that will be active simultaneously. If +this number is exceeded, queries are queued and may be delayed some time. +See also the procq_limit option. +.br +The default for this option is 40. +.TP +.B procq_limit=\fInumber\fP; +When the query thread limit proc_limit is exceeded, connection attempts to pdnsd will be queued. +With this option, you can set the maximum queue length. +If this length is also exceeded, the incoming queries will be dropped. +That means that tcp connections will be closed and udp queries will just be dropped, which +will probably cause the querying resolver to wait for an answer until it times out. +.br +See also the proc_limit option. A maximum of proc_limit+procq_limit +query threads will exist at any one time (plus 3 to 6 threads that will always +be present depending on your configuration). +.br +The default for this option is 60. +.TP +.B tcp_qtimeout=\fItimespec\fP; +This option sets a timeout for tcp queries. If no full query has been received on a tcp connection +after that time has passed, the connection will be closed. The default is set using the +\-\-with\-tcp\-qtimeout option to configure. +.TP +.B par_queries=\fInumber\fP; +This option used to set the maximum number of remote servers that would be queried simultaneously, +for every query that pdnsd receives. +.br +Since version 1.1.11, the meaning of this option has changed slightly. +It is now the increment with which the number of parallel queries is +increased when the previous set of servers has timed out. +For example, if we have a list \fIserver1, server2, server3,\fP etc. of available servers +and par_queries=2, then pdnsd will first send queries to \fIserver1\fP and \fIserver2\fP, +and listen for responses from these servers. +.br +If these servers do not send a reply within their timeout period, pdnsd will send additional +queries to \fIserver3\fP and \fIserver4\fP, and listen for responses from +\fIserver1, server2, server3\fP and \fIserver4\fP, and so on until a useful reply is +received or the list is exhausted. +.br +In the worst case there will be pending queries to all the servers in the list of available servers. +We may be using more system resources this way (but only if the first servers in the list +are slow or unresponsive), but the advantage is that we have a greater chance of catching a reply. +After all, if we wait longer anyway, why not for more servers. +.br +See also the explanation of the global timeout option below. +.br +1 or 2 are good values for this option. +The default is set at compile time using the \-\-with\-par\-queries option to configure. +.TP +.B timeout=\fItimespec\fP; +This is the global timeout parameter for dns queries. +This specifies the minimum period of time pdnsd will wait after sending the +first query to a remote server before giving up without having +received a reply. The timeout options in the configuration file are +now only minimum timeout intervals. Setting the global timeout option +makes it possible to specify quite short timeout intervals in the +server sections (see below). This will have the effect that pdnsd will start +querying additional servers fairly quickly if the first servers are +slow to respond (but will still continue to listen for responses from +the first ones). This may allow pdnsd to get an answer more quickly in +certain situations. +.br +If you use query_method=tcp_udp it is recommended that +you make the global timeout at least twice as large as the largest +server timeout, otherwise pdnsd may not have time to try a UDP query +if a TCP connection times out. +.br +Default value is 0. +.TP +.B randomize_recs=(on|off); +If this option is turned on, pdnsd will randomly reorder the cached records of one type +when creating an answer. This supports round-robin DNS schemes and increases fail +safety for hosts with multiple IP addresses, so this is usually a good idea. +.br +On by default. +.TP +.B query_port_start=(\fInumber\fP|none); +If a number is given, this defines the start of the port range used for queries of pdnsd. The +value given must be >= 1024. The purpose of this option is to aid certain firewall +configurations that are based on the source port. Please keep in mind that another application +may bind a port in that range, so a stateful firewall using target port and/or process uid may +be more effective. In case a query start port is given pdnsd uses this port as the first port of a +specified port range (see query_port_end) used for queries. +pdnsd will try to randomly select a free port from this range as local port for the query. +.br +To ensure that there are enough ports for pdnsd to use, the range between query_port_start and +query_port_end should be adjusted to at least (par_queries * proc_limit). +A larger range is highly recommended for security reasons, and also because other applications may +allocate ports in that range. If possible, this range should be kept out of the space +that other applications usually use. +.br +The default for this option is 1024. Together with the default value of query_port_end, +this makes it the hardest for an attacker to guess the source port used by the pdnsd resolver. +If you specify none here, pdnsd will let the kernel choose the source port, but +this may leave pdnsd more vulnerable to an attack. +.TP +.B query_port_end=\fInumber\fP; +Used if query_port_start is not none. Defines the last port of the range started by query_port_start +used for querys by pdnsd. The default is 65535, which is also the maximum legal value for this option. +For details see the description of query_port_start. +.TP +.B delegation_only=\fIstring\fP; +\fIAdded by Paul Rombouts\fP: This option specifies a "delegation-only" zone. +This means that if pdnsd receives a query for a name that is in a +subdomain of a "delegation-only" zone but the remote name server +returns an answer with an authority section lacking any NS RRs for +subdomains of that zone, pdnsd will answer NXDOMAIN (unknown domain). +This feature can be used for undoing the undesired effects of DNS +"wildcards". Several "delegation-only" zones may be specified together. +If you specify root servers in a server section it is +important that you set root_server=on in such a section. +.br +Example: + +delegation_only="com","net"; + +This feature is off by default. It is recommended that you only use +this feature if you actually need it, because there is a risk that +some legitimate names will be blocked, especially if the remote +name servers queried by pdnsd return answers with empty authority +sections. +.TP +.B ipv4_6_prefix=\fIstring\fP; +This option has the same effect as the \-i command-line option. +When pdnsd runs in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 addresses in +the configuration file (or addresses specified with pdnsd\-ctl) +to IPv6-mapped addresses. +The string must be a valid IPv6 address. Only the first 96 bits are used. +Note that this only effects the parsing of IPv4 addresses listed after this option. +.br +The default is "::ffff.0.0.0.0". +.TP +.B use_nss=(on|off); +If this option is turned on, pdnsd will call initgroups() to set up the group access list, +whenever pdnsd changes its user and group id (see run_as option). +There is a possible snag, though, if initgroups() uses NSS (Name Service Switch) and +NSS in turn uses DNS. In such a case you may experience lengthy timeouts and stalls. +By setting use_nss=off, you can disable the initgroups() call +(only possible in versions 1.2.5 and later). +.br +This option was contributed by Jan-Marek Glogowski. +.br +On by default. +.TP +.B udpbufsize=\fInumber\fP; +\fINew in version 1.2.9:\fP +This option sets the upper limit on the size of UDP DNS messages. The default is 1024. +.br +See also the edns_query server option below. + +.SS server Section +Each server section specifies a set of name servers that pdnsd should try to get +resource records or authoritative name server information from. The servers are +queried in the order of their appearance (or parallel to a limited extend). +If one fails, the next one is taken and so on. +.br +You probably want to specify the dns server in your LAN, the caching dns servers +of your internet provider or even a list of root servers in one or more server sections. +.br +The supported options in this section are: + +.TP +.B label=\fIstring\fP; +Specify a label for the server section. This can be used to refer to this section +when using pdnsd\-ctl, the pdnsd control utility. +.br +You can give several server sections the same label, but if you want to change the addresses +of a server section (see \fBip\fP option below) during run-time with +"pdnsd\-ctl\ server\ \fIlabel\fP\ up\ \fIdns1\fP,\fIdns2\fP,...", +the label must be unique. +.TP +.B ip=\fIstring\fP; +Give the IP (the address, \fInot\fP the host name) of the server. +.br +Multiple IP addresses can be given per server section. +This can be done by entering multiple lines of the form ip=\fIstring\fP; +or a single line like this: + +ip=\fIstring\fP,\fIstring\fP,\fIstring\fP; + +IP addresses do not have to be specified in the configuration file. +A server section without IP addresses will remain inactive until it is assigned +one or more addresses with pdnsd\-ctl, +the pdnsd control utility. +.br +If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses you specify +here will be skipped with a warning message, unless pdnsd is running in IPv6 mode. +Thus, unless pdnsd was compiled to startup in IPv6 mode by default, you need to use the +command-line option \-6 or set run_ipv4=off +first (see global section) in order to ensure +that IPv6 addresses are parsed correctly. +.br +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, +it will automatically be mapped to an IPv6 address. +.TP +.B file=\fIstring\fP; +\fINew in version 1.2:\fP This option allows you to give the name of a resolv.conf-style file. +Of the lines beginning with the nameserver keyword, the second field will be parsed as an +IP address, as if it were specified with the ip= option. The remaining lines will be ignored. +If the contents of the file changes while pdnsd is running, you can make pdnsd aware of the changes through the +use of pdnsd\-ctl, the pdnsd control utility. +This is usually most conveniently done by placing the command "pdnsd\-ctl\ config" in a script +that is automatically run whenever the DNS configuration changes. +.br +For example, suppose you have a ppp client that writes the DNS configuration for your ISP to the file +/etc/ppp/resolv.conf and runs the script /etc/ppp/ip-up when a new +connection is established. One way of ensuring that pdnsd is automatically reconfigured is to +add a server section in the config file with file=/etc/ppp/resolv.conf and to +add the command "pdnsd\-ctl\ config" to /etc/ppp/ip-up. +.TP +.B port=\fInumber\fP; +Give the port the remote name server listens on. Default is 53 (the official +dns port) +.TP +.B uptest=(ping|none|if|dev|diald|exec|query); +Determine the method to check whether the server is available. Currently +defined methods are: +.IP +\(bu \fBping\fP: Send an ICMP_ECHO request to the server. If it doesn't respond +within the timeout, it is regarded to be unavailable until the next probe. +.IP +\(bu \fBnone\fP: The availability status is not changed, only the time stamp is updated. +.IP +\(bu \fBif\fP: Check whether the interface (specified in the interface= option) is +existent, up and running. This currently works for all "ordinary" +network interfaces, interfaces that disappear when down (e.g. ppp?), +and additionally for Linux isdn interfaces (as of kernel 2.2). Note that +you need a /dev/isdninfo device file (major#45, minor#255), or the +isdn uptest will always fail. +.IP +\(bu \fBdev\fP and \fBdiald\fP: Perform an if uptest, and, if that +was succesful, additionally check whether a program is running that +has locked a given (modem-) device. The needed parameters are an interface (specified as for the if +uptest, e.g. "ppp0") and a device relative to /dev (e.g. +"modem" for /dev/modem specified using the device= option. +pdnsd will then look for a pid file for the given interface in /var/lock (e.g. +/var/run/ppp0.pid) and for a lockfile for the given device (e.g. /var/lock/LCK..modem), +and then test whether the locking process is the process that created the pid file and this process is still +alive. If this is the case, the normal if uptest is executed for the given interface. +.br +The dev option is for pppd dial-on-demand, diald is the same for diald users. +.IP +\(bu \fBexec\fP: Executes a given command in the /bin/sh shell +(as /bin/sh \-c ) +and evaluates the result (the return code of the last command) in the shell's way of handling return codes, +i.e. 0 indicates success, all other indicate failure. The shell's process name will be +uptest_sh. The command is given with the uptest_cmd option (see below). +For secuity issues, also see that entry. +.IP +\(bu \fBquery\fP: \fINew in version 1.2:\fP +This works like the ping test, except it sends an (empty) DNS query to the remote server. +If the server sends a well-formed response back within the timeout period (except SERVFAIL), +it will be regarded as available. +This test is useful if a remote server does not respond to ICMP_ECHO requests at all, +which unfortunately is quite common these days. +It can also happen that a remote server is online but ignores empty DNS queries. +Then you will need the set the query_test_name option (see below). +In many cases this test will be a more reliable indicator of availability +than the ones mentioned before. + +The default value is \fBnone\fP. + +\fBNOTE\fP: If you use on-demand dialing, use none, if, +dev, diald or exec, +since ping or query will send packets +in the specified interval and the interface will thus frequently dial! +.TP +.B ping_timeout=\fInumber\fP; +Sets the timeout for the ping test in tenths of seconds +(this unit is used for legacy reasons; actually the current implementation is +only accurate to a second). +.br +The default is 600 (one minute). +.TP +.B ping_ip=\fIstring\fP; +The IP address for the ping test. The default is the IP of the name server. +.TP +.B query_test_name=\fIstring\fP; +\fINew in version 1.2.9:\fP +Sets the name to be queried when using uptest=query availability test. +If the string is the unquoted constant none, +an empty query is used (this the default), otherwise a query of type A will be +sent for the domain name specified here. It is not necessary for the domain name +to exist or have a record of type A in order for the uptest to succeed. +.br +If the the remote server ignores empty queries, you will probably want to set +query_test_name="." (the root domain). +.TP +.B uptest_cmd=\fIstring\fP,\fIstring\fP; +or +.PD 0 +.TP +.PD +.B uptest_cmd=\fIstring\fP; +Sets the command for the uptest=exec function to the first string. +If the second string is given, it specifies a user with whose user +id and primary group id the command is executed. +.br +This is especially useful if you are executing the server as root, +but do not want the uptest to be performed with root privileges. +In fact, you should never execute the uptest as root if you can help +it. +.br +If the server is running setuid or setgid, the privileges thus gained +are attempted to be dropped even before changing identity to the +specified user to prevent setuid/gid security holes (otherwise, any +user might execute commands as root if you setuid the executable). +.br +\fBNote that this is not always possible, and that pdnsd should never +be installed as setuid or setgid.\fP +The command is executed using /bin/sh, so you should be able to use +shell builtin commands. +.TP +.B interval=(\fItimespec\fP|onquery|ontimeout); +Sets the interval for the server up-test. The default is 900 seconds; +however, a test is forced when a query times out and the timestamp is reset then. +.br +If you specify onquery instead of a timeout, the interface will be +tested before every query. This is to prevent automatically dialing +interfaces (diald/pppd or ippp) to dial on dns queries. It is intended to be +used in connection with an interface-testing uptest ;\-) +.br +Note that using uptest=exec, you might run into performance problems +on slow machines when you use that option. +DON'T use onquery with uptest=ping or +uptest=query, as it may cause delays if the server does not answer +(btw, it doesn't make sense anyway). +Note also that using onquery is no guarantee that the interface +will not be used. When another (reachable) dns server tells pdnsd +to query a third dns server for data, pdnsd will do that and has +no means of checking whether this will dial up the interface or not. +This however should be a rare situation. +.br +\fINew in version 1.2.3:\fP +A third possibility is to specify interval=ontimeout. +In this case the server is not tested at startup/reconfiguration, nor at regular intervals, +but only after a DNS query to a server times out. Certain types of network problems +such as a refused connection will also cause the server to be considered unavailable. +However, once a server is declared dead it is never considered again unless it is revived using a +pdnsd\-ctl config or server command. +The idea behind this option is to minimize uptests by assuming all +servers are available until there is reason to believe otherwise. +.TP +.B interface=\fIstring\fP; +The network interface (or network device, e.g. "eth0") for the uptest=if option. +Must be specified if uptest=if is given. +.TP +.B device=\fIstring\fP; +The (modem-) device that is used for the dev uptest. If you use this for a dial-on-demand +ppp uptest (together with uptest=dev), you need to enter the device you are using for your +pppd here, e.g. modem for /dev/modem. +.br +Must be specified if uptest=dev is given. +.TP +.B timeout=\fItimespec\fP; +Set the timeout for the dns query. The default is 120 seconds. You probably want to set this lower. +.br +Timeouts specified in the configuration file are only treated as the +minimum period of time to wait for a reply. A queries to a remote +server are not canceled until a useful reply has been received, or all +the other queries have timed out or failed. +.br +If you have also set the global timeout option, you may consider setting a fairly small value here. +See the explanation of the timeout option in the global +section for what that means. +.TP +.B purge_cache=(on|off); +In every fetched dns record, there is a cache timeout given, which +specifies how long the fetched data may be cached until it needs to be +reloaded. If purge_cache is set to off, the stale records are not purged +(unless the cache size would be exceeded, in this case the oldest records are purged). +Instead, they are still served if they cannot succesfully be +updated (e.g. because all servers are down). +.br +Default is off. +.TP +.B caching=(on|off); +Specifies if caching shall be performed for this server at all. Default is +on. +.TP +.B lean_query=(on|off); +Specifies whether to use the "lean" query mode. In this mode, only the +information actually queried from pdnsd is resolved and cached. This has +the advantage that usually less cache space is used and the query is +usually faster. In 90% of the cases, only address (A) records are needed +anyway. If switched off, pdnsd will always cache all data about a host +it can find and will specifically ask for all available records +(well, at least it is a good approximation for what it really does ;\-) +This will of course increase the answer packet sizes. +.br +Some buggy name servers may not deliver CNAME records when not asked for +all records. I do not know if such servers are around, but if you have +trouble resolving certain host names, try turning this option off. +.br +A last note: If you use multiple pdnsd's that access each other, turning +this option on is probably a big win. +.br +This on by default. +.TP +.B edns_query=(on|off); +\fINew in version 1.2.9:\fP +Specifies whether to use EDNS (Extension mechanisms for DNS) for outgoing queries. +Currently this is only useful for allowing UDP message sizes larger than 512 bytes. +Note that setting this option on can give problems in combination with some legacy +systems or software, including, embarrassingly enough, previous versions of pdnsd. +.br +The default is off, but if your network can handle UDP payloads +significantly larger than 512 bytes, the recommended value is on. +.br +Note that this option only effects outgoing queries. If pdnsd receives a query using +EDNS, it will reply using EDNS regardless of the value of this option. + +See also the udpbufsize option above. +.TP +.B scheme=\fIstring\fP; +You can specify a pcmcia-cs scheme that is used in addition to the uptests. If you specify +a scheme here, the server this section is for will only be queries if the given scheme +is active. Shell wildcards (* and ?) are allowed in the string under their special +meanings. You need to use the scheme_file option on the global +section to make this option work. +.TP +.B preset=(on|off); +This allows you to specify the initial state of a server before any uptest is performed. +on specifies that the server is regarded available. The default is on. +This is especially useful when you set uptest=none; and want to change +the status of a server only via pdnsd\-ctl. +.TP +.B proxy_only=(on|off); +When this option is set to on, answers given by the servers are always accepted, and no +other servers (as, for example, specified in the NS records of the query domain) are +queried. If you do not turn this option on, pdnsd will do such queries in some cases +(in particular when processing ANY queries). +.br +This option is useful when you do not want pdnsd to make connections to outside servers +for some reasons (e.g. when a firewall is blocking such queries). +.br +I recommend that you turn on lean_query when using this option. +.br +Default is off. +.TP +.B root_server=(on|off|discover); +Set this option to on if the servers specified in a section are root servers. +A root server will typically only give the name servers for the top-level domain in its reply. +Setting root_server=on will cause pdnsd to try to use cached information about +top-level domains to reduce to number of queries to root servers, making the resolving of +new names more efficient. +You can get a list of available root servers by running the command +"dig\ .\ ns". +.br +This option is also necessary if you use the delegation_only option. +.br +\fINew in version 1.2.8:\fP This option may also be set to "discover". +This will cause pdnsd to query the servers provided with the ip= option +to obtain the full list of root servers. The root-server addresses will replace the addresses +specified with the ip= option. +This will only be done once on startup, or after a "pdnsd\-ctl\ config" command. +In this case the name servers specified with the ip= option don't have to be +root servers, they just have to know the names and addresses of the root servers. +After root-server discovery pdnsd will behave just as if root_server=on +had been specified. +.br +Default is off. +.TP +.B randomize_servers=(on|off); +\fINew in version 1.2.6:\fP Set this option to on to give each name server +in this section an equal chance of being queried. If this option is off, the name servers +are always queried starting with the first one specified. Even with this option on, the +query order is not truly random. Only the first server is selected randomly; the following +ones are queried in consecutive order, wrapping around to the beginning of the list when +the end is reached. Note that this option only effects the order within a section. The +servers in the first (active) section are always queried before those in the second one, +etc. +.br + The default is off, but if you are resolving from root servers setting this +option on is highly recommended. If root_server=on this option also effects +the query order of the name servers for the top-level domains. +.TP +.B reject=\fIstring\fP; +\fINew in version 1.2.6:\fP This option can be used to make pdnsd reject replies that +contain certain IP addresses. You can specify a single IP address, which will be matched +exactly, or a range of addresses using an address/mask pair. +The mask can be specified as a simple integer, indicating the number of initial 1 bits in +the mask, or in the usual IP address notation. IP addresses may be either IPv4 or IPv6 +(provided there is sufficient support in the C libraries and support for AAAA records was +not disabled). +When addresses in the reject list are compared with those in a reply, only the bits +corresponding to those set in the netmask are significant, the rest are ignored. +.br +Multiple addresses or address/mask pairs may be specified; this can be done by entering +multiple lines of the form reject=\fIstring\fP; +or a single line like this: + +reject=\fIstring\fP,\fIstring\fP,\fIstring\fP; + +How pdnsd reacts when an address in the reply matches one in the reject list, +depends on the reject_policy option, see below. +.TP +.B reject_policy=(fail|negate); +\fINew in version 1.2.6:\fP +This option determines what pdnsd does when an address in the reply from a name server +matches the reject list (see above). If this option is set to +fail, pdnsd will try another server, or, if there no more servers to try, +return the answer SERVFAIL. If this option is set to negate, pdnsd will +immediately return the answer NXDOMAIN (unknown domain) without querying additional +servers. The fail setting is useful if you don't always trust the servers in +this section, but do trust the servers in the following section. The negate +setting can be used to completely censor certain IP addresses. In this case you should put +the same reject list in every server section, and also set the +reject_recursively option (see below) to true. +.br +The default is fail. +.TP +.B reject_recursively=(on|off); +\fINew in version 1.2.6:\fP Normally pdnsd checks for addresses in the +reject list (see above) only when the reply comes directly from a name server +listed in the configuration file. With this option set to on, pdnsd will +also do this check for name servers that where obtained from NS records in the authority +section of a previous reply (which was incomplete and non-authoritative). +.br +Default is off. +.TP +.B policy=(included|excluded|simple_only|fqdn_only); +pdnsd supports inclusion/exclusion lists for server sections: with include= +and exclude= (see below) you can specify domain names for which this server +will be used or will not be used. The first match counts (i.e., the first include or +exclude rule in a server section that matches a domain name is applied, and the +search for other rules is terminated). If no rule matched a given domain name, +the policy= option determines whether this server is used for the +lookup for that domain name; when included is given, the server will +be asked, and when excluded is given, it will not. +If simple_only is given the server will be used if the name to lookup +is a simple (single-label) domain name, on the other hand if fqdn_only +is given the server will be used only for names consisting of two or more labels +(i.e. the name has at least one dot in-between). +.br +If no server is available for a queried domain, pdnsd will return an error message +to the client that usually will stop the client's attempts to resolve a specific +domain from this server (the libc resolver will e.g. return an error to the application that +tried to resolve the domain if no other servers are available in the resolv.conf). +This may be of use sometimes. +.br +\fINote\fP: the simple_only and fqdn_only constants +were added by Paul Rombouts. +They are useful for controlling which name servers (if any) will be used by +pdnsd for resolving simple (single-label) host names. +fqdn_only used to stand for "fully qualified domain name only", but this is +actually a misnomer. The names in queries received by pdnsd are always considered to be +fully qualified. If you do not exactly understand what the options simple_only and +fqdn_only are good for, you are probably better off not using them. +.br +The default for this option is included. +.TP +.B include=\fIstring\fP; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is queried if this was the first matching rule +(see also the entry for policy). +.br +If the given name starts with a dot, the whole subdomain +of the given name including the one of that name is matched, e.g. ".foo.bar." +will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +.br +If it does not start in a dot, only exactly the given name (ignoring the case, of course) +will be matched (hint: if you want to include all subdomains, but not the domain of the given +name itself, place an exact-match exclude rule before the include rule, e.g: +exclude="foo.bar."; include=".foo.bar."; +.br +Previous versions of pdnsd +required that names given with this and the next option ended in a dot, but since +version 1.1.8b1-par8, pdnsd automatically adds a dot at the end if it +is missing. +.br +pdnsd now also accepts a more compact notation for adding several "include" entries in +one line, e.g.: + +include=".foo",".bar",".my.dom"; + +.TP +.B exclude=\fIstring\fP; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is not queried if this was the first matching rule +(see also the entry for policy). +.br +If the given name starts with a dot, the whole subdomain +of the given name including the one of that name is matched, e.g. ".foo.bar." +will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +.br +If it does not start in a dot, only exactly the given name (ignoring the case, of course) +will be matched (hint: if you want to exclude all subdomains, but not the domain of the given +name itself, place an exact-match include rule before the exclude rule, e.g: +include="foo.bar."; exclude=".foo.bar."; +.br +pdnsd now also accepts a more compact notation for adding several "exclude" entries in +one line, e.g.: + +exclude=".foo",".bar",".my.dom"; + + +.SS rr Section +Every rr section specifies a dns resource record that is stored locally. It +allows you to specify own dns records that are served by pdnsd in a limited way. +Only A, PTR, CNAME, MX, NS and SOA records are implemented. +.br +This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. +and localhost. (and perhaps even one or two hosts) without having to start an +extra named if your cached name servers do not serve those records. +It is \fBNOT\fP intended and not capable to work as a full-featured name server. + +.TP +.B name=\fIstring\fP; +Specifies the name of the resource records, i.e. the domain name of +the resource the record describes. This option must be specified +before any a, ptr, cname, +mx, ns or soa records. +Names are interpreted as absolute domain names +(i.e. pdnsd assumes they end in the root domain). +For this and all following arguments that take domain names, you need to +specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.br +\fINew in version 1.2:\fP It is also possible to specify a name starting +with the label *. Such a name is called a wildcard. The * in a wildcard +can match one or more labels in a queried name, but only whole labels. +Any other * characters in a wildcard, apart from the leading one, +will only match a literal *. +.br +For example, *.mydomain will match a.mydomain or www.a.mydomain, but not +mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. +*a.mydomain will only match itself. +.br +Before you can specify an rr section with name=*.mydomain +you must define some records for mydomain, typically NS and/or SOA records. +Example: +.DS L + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } +.DE + +In this example, www.mydomain and ftp.mydomain will resolve to the numeric +address 192.168.1.10 (unless you add rr sections explicitly +specifying different addresses for www.mydomain or ftp.mydomain). +If you want mydomain also to resolve to a numeric address, +add an A record to the first rr section. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after this entry. +This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records for this rr section. +This means that pdnsd flags the domain record so that records of this domain that are not +present in the cache are treated as non-existent, i.e. no other servers are queried for +that record type, and an response containing none of those records is returned. This is +most time what people want: if you add an A record for a host, and it has no AAAA record +(thus no IPv6 address), you normally don't want other name servers to be queried for it. +.br +This is on by default. +.br +Please note that this only has an effect if it precedes the name option! +.TP +.B reverse=(on|off); +\fINew in version 1.2:\fP If you want a locally defined name to resolve to a numeric address +and vice versa, you can achieve this by setting reverse=on before defining the A record +(see below). The alternative is to define a separate PTR record, but you will +probably find this option much more convenient. +.br +The default is off. +.TP +.B a=\fIstring\fP; +Defines an A (host address) record. The argument is an IPv4 address in dotted notation. +pdnsd will serve this address for the host name given in the name option. +.br +Provided there is sufficient support in the C libraries and support for AAAA records was not +disabled, the argument string may also be an IPv6 address, in which case an AAAA record +will be defined. +.br +This option be may used multiple times within an rr section, causing +multiple addresses to be defined for the name. However, if you put the different addresses +in different rr sections for the same name, the definition in the last +rr section will cancel the definitions in the previous ones. +.TP +.B ptr=\fIstring\fP; +Defines a PTR (domain name pointer) record. The argument is a host name in +dotted notation (see name). The ptr record is for resolving adresses into names. For example, if +you want the adress 127.0.0.1 to resolve into localhost, and localhost into 127.0.0.1, you need something +like the following sections: +.br +.DS L + + rr { + name = localhost; + a = 127.0.0.1; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } +.DE + +The second section is for reverse resolving and uses the ptr option. +Note that you can get the same effect by specifying only the first rr section +with reverse=on. +.br +There is something special about the name in the second section: +when a resolver wants to get a host name from an internet address, +it composes an address that is built of the IP address in reverse byte order +(1.0.0.127 instead of 127.0.0.1) where each byte of the adress written +as number constitutes a sub-domain under the domain in-addr.arpa. +.br +So, if you want to compose an adress for reverse resolving, take your ip in dotted notation (e.g. 1.2.3.4), +reverse the byte order (4.3.2.1) and append in-addr.arpa. (4.3.2.1.in-addr.arpa.) +Then, define an rr section giving this address as name and the domain name corresponding to +that ip in the ptr option. +.TP +.B cname=\fIstring\fP; +Defines a CNAME (canonical name) record. +The argument should be a fully-qualified host name in dotted notation (see name). +A CNAME is the DNS equivalent of an alias or symbolic link. +.br +A useful application for CNAMEs is giving short, easy to remember nicknames to hosts with complicated names. +For example, you might want the name "news" to refer to your ISP's news server "nntp2.myisp.com". +Instead of adding an A record for "news" with the same address as "nntp2.myisp.com", you could +put in a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news server changes, +there is no need to update the record for "news". +.br +To implement this with pdnsd, you could add the following section to your configuration file: +.br +.DS L + + rr { + name = news; + cname = nntp2.myisp.com; + owner = localhost; + } +.DE + +.TP +.B mx=\fIstring\fP,\fInumber\fP; +Defines an MX (mail exchange) record. The string is the host name of the mail server in dotted notation (see name). +The number specifies the preference level. +.br +When you send mail to someone, your mail typically goes from your E-mail client to an SMTP server. +The SMTP server then checks for the MX record of the domain in the E-mail address. +For example, with joe@example.com, it would look for the MX record for example.com and find +that the name of mail server for that domain is, say, mail.example.com. +The SMTP server then gets the A record for mail.example.com, and connects to the mail server. +.br +If there are multiple MX records, the SMTP server will pick one based on the preference level +(starting with the lowest preference number, working its way up). +.br +Don't define MX records with pdnsd unless you know what you're doing. +.TP +.B owner=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B ns=\fIstring\fP; +Defines an NS (name server) record. Specifies the name of the host which should be authoritative for the records +you defined in the rr section. This is typically the host pdnsd runs on. +.br +\fINote:\fP In previous versions of pdnsd this option had to be specified before +any a, ptr, cname, mx or soa entries. +In version 1.2, the restrictions on this option are same as the options just mentioned, +and it must listed after the name= option. +This can be a pain if you want to use an old config file which specifies owner= +before name= (sorry about that). +Apart from greater consistency, the advantage is that you can now specify as many NS records as you like (including zero). +.TP +.B soa=\fIstring\fP,\fIstring\fP,\fInumber\fP,\fItimespec\fP,\fItimespec\fP,\fItimespec\fP,\fItimespec\fP; +This defines a soa (start of authority) record. The first string is the +domain name of the server and should be equal to the name you specified as +owner. +.br +The second string specifies the email address of the maintainer of the name +server. It is also specified as a domain name, so you will have to replace the +@ sign in the name with a dot (.) to get the name you have to specify here. +The next parameter (the first number) is the serial number of the record. You +should increment this number if you change the record. +.br +The 4th parameter is the refresh timeout. It specifies after what amount +of time a caching server should attempt to refresh the cached record. +.br +The 5th parameter specifies a time after which a caching server should attempt +to refresh the record after a refresh failure. +.br +The 6th parameter defines the timeout after which a cached record expires if it +has not been refreshed. +.br +The 7th parameter is the ttl that is specified in every rr and should be the +same as given with the ttl option (if you do not specify a ttl, use the default 86400). +.TP +.B txt=\fIstring\fP,...,\fIstring\fP; +\fINew in version 1.2.9:\fP +Defines an TXT record. You can specify one or more strings here. + +.SS neg Section +Every neg section specifies a dns resource record or a dns domain that should be +cached negatively locally. Queries for negatively cached records are always answered +immediatley with an error or an empty answer without querying other hosts as long +as the record is valid. The records defined with neg sections remain +valid until they are explicitely invalidated or deleted by the user using +pdnsd\-ctl. +.br +This is useful if a certain application asks periodically for nonexisting hosts or +RR types and you do not want a query to go out every time the cached record has +timed out. Example: Netscape Communicator will ask for the servers news and mail +on startup if unconfigured. If you do not have a dns search list for your network, +you can inhibit outgoing queries for these by specifying +.br +.DS L + + neg { + name = news; + types = domain; + } + neg { + name = mail; + types = domain; + } +.DE + +in your config file. If you have a search list, you have to repeat that for any +entry in your search list in addition to the entries given above! +.br +In versions 1.1.11 and later, if you negate whole domains this way, all subdomains +will be negated as well. Thus if you specify +.br +neg {name=example.com; types=domain;} in the +config file, this will also negate www.example.com, xxx.adserver.example.com, etc. + +.TP +.B name=\fIstring\fP; +Specifies the name of the domain for which negative cache entries are created. +This option must be specified before the types option. +Names are interpreted as absolute domain names (i.e. pdnsd +assumes they end in the root domain). +You need to specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after this entry. +This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B types=(domain|\fIrr_type\fP[,\fIrr_type\fP[,\fIrr_type\fP[,...]]]); +Specifies what is to be cached negatively: domain will cache the whole +domain negatively; alternatively, you can specify a comma-separated list of RR types +which are to be cached negatively. You may specify multiple types options, but +domain and the RR types are mutually exclusive. +.br +The RR types are specified using their official names from the RFC's in capitals, +e.g. A, CNAME, NS, PTR, MX, +AAAA, ... +.br +The command pdnsd\-ctl\ list\-rrtypes will give you a complete list +of those types. pdnsd\-ctl is built along with pdnsd +and will be installed in the same directory as the pdnsd binary during make install. + +.SS source Section +Every source section allows you to let pdnsd read the records from a file in an +/etc/hosts-like format. pdnsd will generate records to resolve the entries +address from its host name and vice versa for every entry in the file. This is +normally easier than defining an rr for every of your addresses, since localhost +and your other FQDNs are normally given in /etc/hosts. +.br +The accepted format is as follows: The #\-sign initiates a comment, the rest of +the line from the first occurence of this character on is ignored. Empty lines +are tolerated. +.br +The first entry on a line (predeceded by an arbitrary number of tabs and spaces) +is the IP in dotted notation, the second entry on one line (separated by the +first by an arbitrary number of tabs and spaces) is the FQDN (fully qualified +domain name) for that ip. The rest of the line is ignored by default (in the original +/etc/hosts, it may contain information not needed by pdnsd). + +.TP +.B owner=\fIstring\fP; +Specifies the name of the host pdnsd runs on and that are specified in dns +answers (specifically, nameserver records). +Must be specified before any file entries. +.br +Names are interpreted as absolute domain names (i.e. pdnsd +assumes they end in the root domain). +You need to specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B file=\fIstring\fP; +The string specifies a file name. For every file entry in a source section, +pdnsd will try to load the given file as described above. Failure is indicated +only when the file cannot be opened, malformed entries will be ignored. +.TP +.B serve_aliases=(on|off); +If this is turned on pdnsd will serve the aliases given in a hosts-style file. +These are the third entry in a line of a hosts-style file, which usually give a "short name" for the host. +This may be used to support broken clients without a proper domain-search option. +If no aliases are given in a line of the file, pdnsd behaves as without this option for this line. +.br +This feature was suggested by Bert Frederiks. +.br +It is off by default. +.TP +.B authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records with the data from the hosts file. +Please see the description of the option of the same name in the rr section for a closer description of +what this means. Please note that this only has an effect for files sourced with file options +subsequent to this option. +.br +This is on by default. + +.SS include Section +A configuration file may include other configuration files. +However, only the top-level configuration file may contain global +and server sections, +thus include files are effectively limited to sections that add local definitions to the cache. +.br +Include sections currently only have one type of option, which may be given multiple times within a single section. + +.TP +.B file=\fIstring\fP; +The string specifies a file name. For every file option in an include section, +pdnsd will parse the given file as described above. The file may contain include sections itself, +but as a precaution pdnsd checks that a certain maximum depth is not exceeded to guard against +the possibility of infinite recursion. + +.SH "VERSION" +.PP +This man page is correct for version @fullversion@ of pdnsd. +.SH "SEE ALSO" +.PP +.BR pdnsd (8), +.BR pdnsd\-ctl (8) +.PP +More documentation is available in the \fBdoc/\fP subdirectory of the source, +or in \fB/usr/share/doc/pdnsd/\fP if you are using a binary package. + +.SH AUTHORS + +\fBpdnsd\fP was originally written by Thomas Moestl +.UR + +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later). +.PP +Several others have contributed to \fBpdnsd\fP; see files in the source or +\fB/usr/share/doc/pdnsd/\fP directory. +.PP +This man page was automatically generated from the html documentation for \fBpdnsd\fP, +using a customized Perl script written by Paul A. Rombouts. +.PP +Last revised: 19 April 2012 by Paul A. Rombouts diff --git a/app/src/main/jni/pdnsd/doc/pdnsd.conf.in b/app/src/main/jni/pdnsd/doc/pdnsd.conf.in new file mode 100644 index 0000000..e348eb0 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/pdnsd.conf.in @@ -0,0 +1,143 @@ +// Sample pdnsd configuration file. Must be customized to obtain a working pdnsd setup! +// Read the pdnsd.conf(5) manpage for an explanation of the options. +// Add or remove '#' in front of options you want to disable or enable, respectively. +// Remove '/*' and '*/' to enable complete sections. + +global { + perm_cache=1024; + cache_dir="@cachedir@"; +# pid_file = /var/run/pdnsd.pid; + run_as="@def_id@"; + server_ip = 127.0.0.1; # Use eth0 here if you want to allow other + # machines on your network to query pdnsd. + status_ctl = on; +# paranoid=on; # This option reduces the chance of cache poisoning + # but may make pdnsd less efficient, unfortunately. + query_method=udp_tcp; + min_ttl=15m; # Retain cached entries at least 15 minutes. + max_ttl=1w; # One week. + timeout=10; # Global timeout option (10 seconds). + neg_domain_pol=on; + udpbufsize=1024; # Upper limit on the size of UDP messages. +} + +# The following section is most appropriate if you have a fixed connection to +# the Internet and an ISP which provides good DNS servers. +server { + label= "myisp"; + ip = 192.168.0.1; # Put your ISP's DNS-server address(es) here. +# proxy_only=on; # Do not query any name servers beside your ISP's. + # This may be necessary if you are behind some + # kind of firewall and cannot receive replies + # from outside name servers. + timeout=4; # Server timeout; this may be much shorter + # that the global timeout option. + uptest=if; # Test if the network interface is active. + interface=eth0; # The name of the interface to check. + interval=10m; # Check every 10 minutes. + purge_cache=off; # Keep stale cache entries in case the ISP's + # DNS servers go offline. + edns_query=yes; # Use EDNS for outgoing queries to allow UDP messages + # larger than 512 bytes. May cause trouble with some + # legacy systems. +# exclude=.thepiratebay.org, # If your ISP censors certain names, you may +# .thepiratebay.se, # want to exclude them here, and provide an +# .piratebay.org, # alternative server section below that will +# .piratebay.se; # successfully resolve the names. +} + +/* +# The following section is more appropriate for dial-up connections. +# Read about how to use pdnsd-ctl for dynamic configuration in the documentation. +server { + label= "dialup"; + file = "/etc/ppp/resolv.conf"; # Preferably do not use /etc/resolv.conf + proxy_only=on; + timeout=4; + uptest=if; + interface = ppp0; + interval=10; # Check the interface every 10 seconds. + purge_cache=off; + preset=off; +} +*/ + +/* +# The servers provided by OpenDNS are fast, but they do not reply with +# NXDOMAIN for non-existant domains, instead they supply you with an +# address of one of their search engines. They also lie about the addresses of +# of the search engines of google, microsoft and yahoo. +# If you do not like this behaviour the "reject" option may be useful. +server { + label = "opendns"; + ip = 208.67.222.222, 208.67.220.220; + reject = 208.69.32.0/24, # You may need to add additional address ranges + 208.69.34.0/24, # here if the addresses of their search engines + 208.67.219.0/24; # change. + reject_policy = fail; # If you do not provide any alternative server + # sections, like the following root-server + # example, "negate" may be more appropriate here. + timeout = 4; + uptest = ping; # Test availability using ICMP echo requests. + ping_timeout = 100; # ping test will time out after 10 seconds. + interval = 15m; # Test every 15 minutes. + preset = off; +} +*/ + +/* +# This section is meant for resolving from root servers. +server { + label = "root-servers"; + root_server = discover; # Query the name servers listed below + # to obtain a full list of root servers. + randomize_servers = on; # Give every root server an equal chance + # of being queried. + ip = 198.41.0.4, # This list will be expanded to the full + 192.228.79.201; # list on start up. + timeout = 5; + uptest = query; # Test availability using empty DNS queries. +# query_test_name = .; # To be used if remote servers ignore empty queries. + interval = 30m; # Test every half hour. + ping_timeout = 300; # Test should time out after 30 seconds. + purge_cache = off; +# edns_query = yes; # Use EDNS for outgoing queries to allow UDP messages + # larger than 512 bytes. May cause trouble with some + # legacy systems. + exclude = .localdomain; + policy = included; + preset = off; +} +*/ + +source { + owner=localhost; +# serve_aliases=on; + file="/etc/hosts"; +} + +/* +include {file="/etc/pdnsd.include";} # Read additional definitions from /etc/pdnsd.include. +*/ + +rr { + name=localhost; + reverse=on; + a=127.0.0.1; + owner=localhost; + soa=localhost,root.localhost,42,86400,900,86400,86400; +} + +/* +neg { + name=doubleclick.net; + types=domain; # This will also block xxx.doubleclick.net, etc. +} +*/ + +/* +neg { + name=bad.server.com; # Badly behaved server you don't want to connect to. + types=A,AAAA; +} +*/ diff --git a/app/src/main/jni/pdnsd/doc/txt/doc_makefile b/app/src/main/jni/pdnsd/doc/txt/doc_makefile new file mode 100644 index 0000000..57aa7c1 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/txt/doc_makefile @@ -0,0 +1,21 @@ +# Makefile for converting pdnsd html documentation to text files. +# This file was based on a Makefile originally written by Thomas Moestl +# and adapted by Paul Rombouts. + + +HTML2TXT=w3m -dump -cols 80 -T text/html + +doc: intro.txt manual.txt faq.txt +.PHONY: doc clean + +intro.txt: ../html/index.html + sed -e 's///-->/g' ../html/index.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > intro.txt + +manual.txt: ../html/doc.html + sed -e 's///-->/g' ../html/doc.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > manual.txt + +faq.txt: ../html/faq.html + sed -e 's///-->/g' ../html/faq.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > faq.txt + +clean: + @rm -fv intro.txt manual.txt faq.txt diff --git a/app/src/main/jni/pdnsd/doc/txt/faq.txt b/app/src/main/jni/pdnsd/doc/txt/faq.txt new file mode 100644 index 0000000..0263161 --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/txt/faq.txt @@ -0,0 +1,227 @@ +The pdnsd FAQ + +Q: There are complete and well-tested name servers around, such as the BIND. + These do also perform caching. Why should I use pdnsd? + + pdnsd does not aim to be a complete name server implementation, such as the + BIND. It is optimized for caching, and you can only specify a small subset + of all dns record types pdnsd knows in your local "zone" definitions. This + of course reduces the code size drastically, and such the memory footprint. + There are some features especially interesting for dialin networks, + ordinary (non-server) internet hosts and computers that are often not + connected to to their network, e.g. notebooks (I originally wrote this + program for use with my notebook). These features are: +A: + * permanent disk cache (useful for frequent power-offs/reboots) + * usually smaller memory footprint (depends on cache size) (see next + question) + * offline-detection prevents hangs (e.g. the typical hang on startup of + some Netscape Navigator versions if not dialled in) + * better control about timeouts (also to prevent hangs) + * better control over the cache + * better run-time control + +------------------------------------------------------------------------------- + + When I look at the process size with ps, top, gtop, or a similar tool, I +Q: see some processes with a total size well above 3.5 MB. This is much more + than e.g. BIND named (about 1.4 MB). Why? + + Really, it is not. pdnsd uses multithreading, not multiprocessing. That + means that the processes share most of their process space. In the + LinuxThreads library or NPTL (Native Posix Thread Libary), which are used + by pdnsd on Linux, in fact the total process address space is shared +A: (although the processes have different stacks, these are in one process + address space). You may check this by looking at the at the process sizes + of the pdnsd threads: all should be the same. The effective size that pdnsd + occupies is thus the size of any of the processes, not the sum of those. + So, pdnsd with empty cache occupies about 800 kB, and the maximum size + should be about the cache size plus this size (in fact, ca 5-10% more). + +------------------------------------------------------------------------------- + +Q: What do I need the status control (option -s) for? + + It enables you to do some things you might or might not need. With it, you + can: + + * query pdnsd's settings at runtime to debug configuration files and see + which servers are regarded to be available +A: * mark servers as available or unavailable, or force a status retest - + very handy if you want to control which servers pdnsd queries, e.g for + muliple dial-up accounts + * delete, invalidate or add DNS records - useful e.g. when you want to + build records for dynamically assigned IP addresses or domain names + * reload pdnsd's configuration file without restarting pdnsd + * print information about the contents of pdnsd's cache. + +------------------------------------------------------------------------------- + +Q: What do I need local records (rr- and source-sections in the config file) + for? + + Some resolver programs, e.g. nslookup, want to look up the name of the + server they are using before doing anything else. This option is for + defining a PTR record for your IP such that those programs get an answer + even if the name server you are caching is not available or does not offer + these records. By extension, you may also define A and SOA records. This + allows you to build very small zones without having to use a "big" name + server. It is NOT intended to replace such a complete server in anything + but VERY small networks. Alternatively, you may start a named on another + host or on the same host on another port and cache it with pdnsd in + addition to other (more distant) name servers. +A: The source section allows you to let pdnsd read in your /etc/hosts file on + startup and serve its contents. This file is used by your local resolver + before it even tries the name servers and usually contains fully-qualified + domain names (FQDNs) for all of the internet addresses your host has. If + you source this file, you usually won't need any additional rr sections. + Sourcing it also allows other hosts (eg. in your local network) to access + the names defined in your hosts file. You can of course just add other + hosts in your local network to the servers hosts file, thus making them + known to your server's resolver and pdnsd (if you sourced that file). + If you don't know what this answer was all about, you should just take the + source section in the sample config file that comes with pdnsd, copy it + into your config file and forget about it. + +------------------------------------------------------------------------------- + + When compiling, I get an error message like +Q: Please define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN + What's up? + + Normally, this macros should be defined in your C library's header files. + There are two different methods, most C libraries support both (and pdnsd + honors both): either __BYTE_ORDER is set to __LITTLE_ENDIAN or + __BIG_ENDIAN, or __LITTLE_ENDIAN or __BIG_ENDIAN are directly defined as + macros. + Linux glibc, for example, does set those macros correctly. Never mind. You + just have to know whether your machine is little-endian or big-endian, this + means wheter your machine saves the least significant byte of a word or + double-word first in memory (little-endian) or the most significant first +A: (big-endian). All intel x86 and Alpha machines are little-endian, for + example, while SPARC and PowerPC architectures are big-endian. If your + machine is little-endian, add the following line to your config.h: + #define __BYTE_ORDER __LITTLE_ENDIAN + Likewise, if your machines byte order is big-endian: + #define __BYTE_ORDER __BIG_ENDIAN + Pathological byte orders like pdp-endian are not yet supported really; + However, for the place the endianess is needed, __LITTLE_ENDIAN should do + (it deals only with 16 bits; for all other occurances, ntoh[sl]/hton[sl] is + used). + +------------------------------------------------------------------------------- + + At startup, I get a warning saying: +Q: Uptest command [...] will implicitly be executed as root + What does that mean? + + This warning only occurs if you use the uptest=exec option in your + configuration. It means that the uptest command is run as root because + pdnsd is running as root, and this was not explicitely specified. The idea + is that it may introduce security holes (in the programs being run) when +A: they run as root, and so they shouldn't do that if possible. You can + specify the user that shall run the command by appending its name + comma-separated as string to the uptest_cmd line: + uptest_cmd="",""; + If it is correctly running as root, just append the user string "root" to + the command and the warning will not occur again. + +------------------------------------------------------------------------------- + +Q: I cannot run my uptest_cmd command as root (it says permission denied), + although the pdnsd executable is setuid root. Why? + + pdnsd will drop privileges gained through setuid/setgid before executing + the uptest commands (you shouldn't set the pdnsd executable setuid/setgid +A: anyway). The reason is clear: if you install the pdnsd executable as setuid + root and this wouln't be done, any user could execute shellcode with root + privileges using that option! + +------------------------------------------------------------------------------- + + At startup, I get an error saying: +Q: Bad config file permissions: the file must be only writeable by the user + Why is that? + + pdnsd has an option (uptest=exec) that allows the execution of arbitrary + shell code (for testing whether an interface is up). This must be of course + secured against unauthorized use. One of these protection is the one that + produces the error message: if you routinely run pdnsd, e.g. at system + startup, and your config file is editable for others, someone could change +A: it and insert shell code that is executed in the next pdnsd run -- with + your user privileges! To prevent this, pdnsd will exit if the config file + is writeable by others than the owner. To get rid of this message, just do + chmod go-w + on your config file (for the default file: chmod go-w /etc/pdnsd.conf). You + should also check that the ownership is set correct. + +------------------------------------------------------------------------------- + +Q: serve_aliases does not seem to work. + + Some resolvers (e.g. of the glibc 2.1) seem sometimes not to look up + unmodified names, but the names with an entry of the search path already +A: appended. Since pdnsd will serve short names with this option anyway, you + can delete the search an domain options from your /etc/resolv.conf. This is + reported to work in some cases. + +------------------------------------------------------------------------------- + +Q: Some queries for domains that have many records (e.g. www.gmx.de) fail + mysteriously. + + pdnsd versions prior to 1.1.0 had the tcp server thread disabled by + default. Most resolvers repeat their query using tcp when they receive a +A: truncated answer (the answer is truncated when it exceeds a length of 512 + bytes). You need to recompile pdnsd with the option --enable-tcp-server to + fix this. + +------------------------------------------------------------------------------- + + I am behind some kind of firewall. In the configuration file I have only +Q: listed addresses of name servers on the local (ISP's) network, but pdnsd is + slow and DNS queries frequently time out. + + In some cases pdnsd will not consider the answer of the local name server + authoritative enough, and will try to get answers from the name servers + listed in the authority section of the reply message. If pdnsd is behind a + firewall that blocks the UDP reply packets from remote name servers, pdnsd + will wait in vain for a reply. One solution is to set proxy_only=on in the +A: servers sections of the configuration file. This will prevent pdnsd from + querying name servers that are not listed in the configuration file. + Another solution that can be tried is specifying query_method=tcp_only in + the global section of the configuration file, because a firewall that + blocks UDP packets from outside might still allow outgoing TCP connections + to port 53. + +------------------------------------------------------------------------------- + +Q: Is pdnsd vulnerable to DNS cache poisoning as described in CERT + vulnerability note VU#800113? + + Short answer: Yes. + Somewhat longer answer: The problem is not so much that pdnsd's + implementation is flawed but rather that the DNS protocol currently being + used is fundamentally flawed from a security viewpoint. As long as a more + secure protocol is not in place, all that the developers of pdnsd can do is + to try to tweak the current implementation to make it as difficult as + possible for an attacker to succeed. + From version 1.2.7 onwards, the default for the query_port_start option is +A: 1024, which means that the pdnsd resolver will randomly select source ports + in the range 1024-65535. (In previous versions the default was to let the + kernel select the source ports, which will often result in a more or less + predictable sequence of ports.) It also helps to use a good quality source + of random numbers. On platforms where this is supported, it is preferable + to configure with --with-random-device=/dev/urandom. There is still more + that can be done to make pdnsd less vulnerable, but this remains (as of + this writing) a work in progress. + Please note that pdnsd was designed for small (private) networks, and that + it is generally not recommended to let untrusted users access pdnsd. + +------------------------------------------------------------------------------- + +Thomas Moestl and Paul Rombouts + + +Last revised: 18 August 2008 by Paul Rombouts + diff --git a/app/src/main/jni/pdnsd/doc/txt/intro.txt b/app/src/main/jni/pdnsd/doc/txt/intro.txt new file mode 100644 index 0000000..db1c89e --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/txt/intro.txt @@ -0,0 +1,305 @@ +------------------------------------------------------------------------------- + +About pdnsd + +pdnsd is a proxy DNS server with permanent caching (the cache contents are +written to hard disk on exit) that is designed to cope with unreachable or down +DNS servers (for example in dial-in networking). +Since version 1.1.0, pdnsd supports negative caching. + +It is licensed under the GNU General Public License (GPL). This, in short, +means that the sources are distributed togehter with the program, and that you +are free to modify the sources and redistribute them as long as you also +license them under the GPL. You do not need to pay anything for pdnsd. It also +means that there is ABSOLUTELY NO WARRANTY for pdnsd or any part of it. For +details, please read the GPL. + +pdnsd can be used with applications that do DNS lookups, e.g. on startup, and +can't be configured to change that behaviour, to prevent the often minute-long +hangs (or even crashes) that result from stalled DNS queries. Some Netscape +Navigator versions for Unix, for example, expose this behaviour. + +pdnsd is configurable via a file and supports run-time configuration using the +program pdnsd-ctl that comes with pdnsd. This allows you to set the status +flags of servers that pdnsd knows (to influence which servers pdnsd will +query), and the addition, deletion and invalidation of DNS records in pdnsd's +cache. +Parallel name server queries are supported. This is a technique that allows +querying several servers at the same time so that very slow or unavailable +servers will not block the answer for one timeout interval. +Since version 1.0.0, pdnsd has full IPv6 support. + +There is also a limited support for local zone records, intended for defining +1.0.0.127.in-addr.arpa. and localhost. , since some clients request that +information and it must be served even if the cached servers are not available +or do not serve these records. pdnsd may also read your /etc/hosts file (this +file is normally used by your local resolver and usually contains information +for localhost as well as for your machines FQDN) and serve its contents. + +pdnsd was started on Linux, and has since been ported to FreeBSD (and Cygwin +and Darwin). 90% of the source code should be easily portable to POSIX- and +BSD-compatible systems, provided that those systems support the POSIX threads +(pthreads). The rest might need OS-specific rewrites. + +Currently, pdnsd is only compileable by gcc. This should be easy to fix, but I +just do not have documentation for other compilers. If you are not able or do +not want to use gcc, I would recommend you just try to do the minor changes. + +pdnsd must be started as root in some cases (raw sockets are needed for icmp +echoes for the option uptest=ping, and the default port is 53, this must be > +1024 to allow non-root execution). However, pdnsd can be configured to change +it's user and group id to those of a non-privileged user after opening the +sockets needed for this. + +The server should support the full standard DNS queries following the rfcs 1034 +and 1035. As of version 1.0.0, the rfc compliance has been improved again, and +pdnsd is now believed (or hoped?) to be fully rfc-compatible. It completely +follows rfc 2181 (except for one minor issue in the FreeBSD port, see the +documentation). It does not support the following features, of which most are +marked optional, experimental or obsolete in these rfcs: + + * Inverse queries + * Status queries + * Completion queries + * Namespaces other than IN (Internet) + * AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain + zones, that should not violate the standard + +The following record types, that are extensions to the original DNS standard, +are supported for caching since version 1.2.9 (if you do not need most of them, +you can disable runtime support for the unneeded ones before compiling pdnsd +and save a little cache and executable space, see the source file src/ +rr_types.in): + + * RP (responsible person, RFC 1183) + * AFSDB (AFS database location, RFC 1183) + * X25 (X25 address, RFC 1183) + * ISDN (ISDN number/address, RFC 1183) + * RT (route through, RFC 1183) + * NSAP (Network Service Access Protocol address , RFC 1348) + * PX (X.400/RFC822 mapping information, RFC 1995) + * GPOS (geographic position, deprecated) + * AAAA (IPv6 address, RFC 1886) + * LOC (location, RFC 1876) + * EID (Nimrod EID) + * NIMLOC (Nimrod locator) + * SRV (service record, RFC 2782) + * ATMA (ATM address) + * NAPTR (URI mapping, RFC 2168) + * KX (key exchange, RFC 2230) + * CERT (Certificate record, RFC 4398) + * DS (Delegation Signer, RFC 4034) + * RRSIG (Resource Record Signature, RFC 4034) + * NSEC (Next Secure, RFC 4034) + * DNSKEY (record containing the public key for a zone, RFC 4034) + * NSEC3 (Next Secure version 3, RFC 5155) + * NSEC3PARAM (NSEC3 parameters, RFC 5155) + +Note: This list is incomplete. For the complete list see the source file src/ +rr_types.in. + +There are FreeBSD and OpenBSD ports available for pdnsd (ports/net/pdnsd for +both). Thanks go to Roman Shterenzon for the FreeBSD port Sebastian Stark for +the OpenBSD one! Thanks to Kiyo Kelvin Lee now also runs on the Cygwin +platform! Thanks goes to Rodney Brown for extending portability to the Darwin +(Apple Mac OS X) platform! + +If you have questions left, you should take a look into the FAQ. +Bugfixes, patches and compatability fixes for other OSs are very welcome! + +Features in detail + +This section describes some of pdnsds features in detail. Most of the options +are set in the config file. For more information on the configuration file, see +the documenation page. + + +Uptests + +pdnsd provides several methods to test whether a remote DNS server should be +regarded as available (so that pdnsd can query it), in addition to the obvious +"none" test (the server is always regarded as available, or availability is set +on or off using the pdnsd-ctl utility). These tests are: + + * ping: a given adress is ping'ed in a given interval. If it there is no + response or the host is unreachable, the server is seen to be not available + (for those who don't know: pinging is sending a certain Internet packet + type to a host to which any standard-conformant host is required to reply). + * if: a given network interface is tested whether it is existent, up and + running. If it is not, the server is regarded to be not available. This is + especially useful for ppp and similar interfaces. A special case test for + Linux isdn (ippp*) interfaces is integrated, so that the uptests should + also work for these. + * dev: this is a variant of the if uptest for use with Linux dial-on-demand + ppp interfaces. In addition to performing an if-style interface uptest, it + also tests whether a specified program (e.g. pppd) owns a lock to a given + (modem-) device. + * exec: a given shell command line is executed and the exit status of the + whole command line (which is normally the exit status of the last command) + is evaluated. If it is not zero, the server is regarded to be not + available. This is a very flexible testing method with which it should be + able to perform virtually any needed test. + * query: New in version 1.2: This works like the ping test, except it sends + an (empty) DNS query to the remote server. If the server sends a + well-formed response back within the timeout period (except SERVFAIL), it + will be regarded as available. This test is useful if a remote server does + not respond to ICMP_ECHO requests at all, which unfortunately is quite + common these days. In many cases this test will be a more reliable + indicator of availability than the ones mentioned above. + + +Local Records ("Zones") + +As mentioned above, there are only very basic local record types (ie the record +types that you may use in record declarations in your local configuration for +records that pdnsd shall serve in addion to the cached ones). They are +organized roughly in zones but have not complete zone declarations, so I +generally do not use the term "zone" for them, but rather "local records". +These are the local record types pdnsd can understand: + + * SOA (information about the name server) + * A (domain-name-to-address mapping) + * PTR (pointer, used normally for address-to-domain-name mapping) + * NS (name server, generated automatically by pdnsd for any local record set) + * CNAME (canonical host name) + * MX (mail exchange for the domain) + * TXT (arbitrary text strings, often used for Sender Policy Framework) + +You can specify these records in the configuration file. +You may "source" a file in a format like that used in the /etc/hosts file, that +means that pdnsd reads this file, extracts addresses and domain names from it +and automatically generates A records for name to address mapping, PTR records +for address to name mapping and NS records (name server specifiation) for each +entry in the file. +Records can also be changed dynamically at run time. +A script contributed by Marko Stolle makes pdnsd usable in a DHCP setup using +this feature. + +System requirements + +As mentioned, pdnsd currently runs under Linux, FreeBSD and Cygwin. Other BSD +flavours may or may not work (feedback is very welcome!). The system and +software requirements under Linux are: + + * Kernel version >2.2.0 + * glibc version >2.0.1 (aka libc6) with LinuxThreads (normally included) or + NPTL (Native Posix Thread Library, recommended). + Due to a bug, pdnsd 0.9.8 does not run with glibc2.1.1. This behaviour was + fixed in pdnsd 0.9.9. + * For IPv6: glibc>=2.1 + +The system requirements under FreeBSD are: + + * FreeBSD versions >=2.6 (prior ones may or may not work) + * For IPv6: FreeBSD >=4.0 is recommended (no idea if it runs on prior + versions) + +The common software requirements for all supported systems are: + + * GCC, preferably egcs-2.* or 3.* (other compilers are currently not + supported; the needed patch for another compiler should not be difficult, + however) + * GNU or BSD make + * the standard commands install, grep, sed, awk, touch and which (along with + the REALLY standard ones mv, cp, ln, rm, pwd, test, echo, cat, mkdir, + chown, chmod, tar). In any standard Unix installation, this should be no + problem. + * for hacking and building own packages, you might also need gzip, bzip2, + perl and rpmbuild + + +Download + +If you want to download pdnsd, please visit the download page. + +Authors + +pdnsd was originally written by Thomas Moestl, but is no longer maintained by +him. Paul A. Rombouts has revised large portions of the code and has added a +number of new features. See README.par and the ChangeLog in the source +directory (or /usr/share/doc/pdnsd- if you have installed a binary +package) for more details. If you have questions about the recent +modifications, you can find the email address of the current maintainer at the +end of README.par. + +Daniel Smolik has contributed RedHat RPMs (the most recent RPMs are available +here). +Torben Janssen contributed start scripts for Red Hat Linux. +Soenke J. Peters contributed patches and suggestions for Red Hat compatability. +Wolfgang Ocker has contributed the code and documentation for the server_ip +option. +Markus Mohr contributed a Debian rc script. +Nikita V. Youschenko contributed extensions to the "if" uptest. +Lyonel Vincent extended the serve_aliases option to support an arbitrary number +of aliases. +Sourav K. Mandal wrote the autoconf scripts and contributed many fixes and +suggestions. +Stephan Boettcher contributed the SCHEME= option. +Ron Yorston contributed the uptest for Linux ppp dial-on-demand devices. +Alexandre Nunes fixed some bugs in the autoconf files. +Sverker Wiberg contributed fixes for IPv6. +Carsten Block contributed configure-able rc scripts. +Olaf Kirch contributed a security fix for the run_as code. +Paul Wagland contributed various patches for bind9-compatability and other +issues. +Roman Shterenzon contributed patches and lots of helpful hints for FreeBSD +compatability. +Bernd Leibing has contributed spec file fixes. +Michael Wiedmann has contributed the pdnsd-ctl.8 man page. +Marko Stolle has contributed the contrib/pdnsd_update.pl script that makes +pdnsd usable in a DHCP setup. +P.J. Bostley has contributed patches to get pdnsd working on alpha properly. +Christian Engstler contributed patches for SuSE compatability. +Bjoern Fischer contributed code to make pdnsd leave the case of names in the +cache unchanged. +Marko Stolle contributed the contrib/pdnsd_update.pl script that makes pdnsd +usable in a DHCP setup. +Andrew M. Bishop contributed the support for the label server option and the +pdnsd-ctl interface for using it. +Frank Elsner contributed rc script fixes. +Andreas Steinmetz contributed the code for query_port_start and query_port_end +options. +Mahesh T. Pai contributed the pdnsd.8 man page. +Nikola Kotur contributed the Slackware start-up script. +Kiyo Kelvin Lee contributed a patch for Cygwin support. +Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support. +Jan-Marek Glogowski contributed a patch implementing the use_nss option. + +Special thanks to Bert Frederiks for letting me do a late-night debugging run +on his machine to spot obscure bugs! + +Thanks to the following persons for reporting bugs and being helpful: +David G. Andersen, +Dirk Armbrust, +Daniel Black, +Kevin A. Burton, +Juliusz Chroboczek, +Joachim Dorner, +Stefan Erhardt, +Stefan F?rster, +Mike Hammer, +Jonathan Hudson, +Dan Jacobson, +Byrial Jensen, +Patrick Loschmidt, +James MacLean, +Fraser McCrossan, +Michael M?ller, +Erich Reitz, +Brian Schroeder, +Milan P. Stanic, +Michael Steiner, +Norbert Steinl, +Markus Storm, +Michael Str?der, +Alan Swanson, +Eelco Vriezekolk. + + +------------------------------------------------------------------------------- + +Thomas Moestl and Paul A. Rombouts + + +Last revised: 17 March 2012 by Paul A. Rombouts + diff --git a/app/src/main/jni/pdnsd/doc/txt/manual.txt b/app/src/main/jni/pdnsd/doc/txt/manual.txt new file mode 100644 index 0000000..c7e3e9d --- /dev/null +++ b/app/src/main/jni/pdnsd/doc/txt/manual.txt @@ -0,0 +1,2017 @@ + pdnsd Documentation + +This is the "official" pdnsd documentation and reference written by Thomas +Moestl with revisions by Paul A. Rombouts. +This manual is a part of the pdnsd package, and may be distributed in original +or modified form under terms of the GNU General Public License, as published by +the Free Software Foundation; either version 3, or (at your option) any later +version. +You can find a copy of the GNU GPL in the file COPYING in the source or +documentation directory. +This manual is up-to-date for version 1.2.9b. For older documentation, please +refer to the doc directory of the respective pdnsd package. +If you want a quicker introduction to pdnsd, you can try some of the HOWTOs +available on the web. For Apple Mac users, Brian Wells has published a good +HOWTO at http://web.mac.com/brianwells/main/pdnsd.html. + +0. Installation + +0.1 Installing binary RPM's + +To install a binary RPM, just do + +rpm -i pdnsd-.rpm + +This should install pretty much everything automatically. The only thing left +for you to do is adapt your configuration file (stored in /etc/pdnsd.conf) +according to your needs (see below). In the Red Hat and SuSE RPMs, a start +script is also installed; read the section 0.4, Start at Boot Time about that. + +0.2 Building RPM's + +It is possible to build a binary RPM from a source package using the command + +rpmbuild --rebuild pdnsd-.src.rpm + +or alternatively from a tarball using the command + +rpmbuild -tb pdnsd-.tar.gz + +You can do this as root, but it is safer to build a binary package first as a +normal user, and then, when all has gone well, install the resulting binary +package as root as in the previous section. How to build an RPM package without +being root is described at http://www.ibm.com/developerworks/linux/library/ +l-rpm1/. + +Several pdnsd-specific options are available when building RPM packages: + +--with isdn Has the same effect as --enable-isdn (see below). + +--without poll Has the same effect as --disable-poll (see below). + +--without nptl Has the same effect as --with-thread-lib=linuxthreads ( + see below). + +--with ipv6 Has the same effect as --enable-ipv6 (see below). + +--without tcpqueries Has the same effect as --disable-tcp-queries (see below + ). + +--without debug Has the same effect as --with-debug=0 (see below). + +--define "distro < Has the same effect as --with-distribution= ( +distro>" see below). + +--define "run_as_user Has the same effect as --with-default-id= (see +" below). + For RPMs the default is "pdnsd". + + If the user defined by the previous option does not +--define "run_as_uid < exist when the RPM is installed, the pre-install script +uid>" will try to create a new user with numerical id . + The default is to let the system choose the numerical + id at install time. + +--define "cachedir < Has the same effect as --with-cachedir= (see below +dir>" ). + +You can also configure which compiler flags will be used by setting the +environment variable CFLAGS. Using a bash shell, you can do that on the command +line like this: CFLAGS="-O1 -Wall" rpmbuild ... +This is useful if you prefer a different level of optimization, for instance. + +0.3 Installing from pure sources (tar archives or git repositories) + +0.3.1 Setting up the source code tree + +Source code is available in the form of snapshots (tarballs) or a git +repository with the very latest development code and a (nearly) complete +history of all the revisions. Cloning a git repository is useful if you need a +recent fix or feature that is not yet contained in a main release or you want +to participate in pdnsd development. Otherwise you will probably find the +tarballs more convenient because they are much more compact. + +0.3.1.1 Unpacking a tar archive + +The pdsnsd snapshot releases come in the form of a gzip'ed tar archive. To +decompress it (using a modern tar) do + +tar -xzf pdnsd-.tar.gz + +If your tar doesn't do this, use: + +gzip -dc pdnsd-.tar.gz | tar -xf - + +0.3.1.2 Cloning a git repository + +To clone a git repository you need to install, if not already installed, the +git version control system, which is available as a package in most modern +Linux distributions. Then run the command: + +git clone git://gitorious.org/pdnsd/pdnsd.git pdnsd + +In rare cases, if you are behind some kind of firewall, the special git +protocol can't be used and you will need to fall back to the http protocol. See +the gitorious.org website or git documentation for more information. + +0.3.2 Configuring the source + +Change into the pdnsd source directory and run configure. It takes the +following command line options (if you do not specify an option, defaults will +be used): + + Specify the prefix directory. The pdnsd files are + installed in subdirectories of the prefix, the +--prefix=dir pdnsd and pdnsd-ctl executables are for example + installed in the sbin subdirectory of the prefix. + The default for this is /usr/local; you might want + to set this to /usr (using --prefix=/usr). + + Specify the config directory. pdnsd expects its + pdnsd.conf file to reside there if the -c option is +--sysconfdir=dir not given at startup. The default for this is the + etc subdirectory of your prefix, e.g. /usr/local/ + etc if you did not specify a prefix. To set this + e.g. to /etc, use --sysconfdir=/etc. + +--with-distribution= Specify target distribution (default=Generic; +distro others: RedHat, SuSE, Debian) + See below for the effect of these settings. + + Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin). + autodetect will attempt to detect whether you are +--with-target=platform using Linux, *BSD or Cygwin and should normally be + sufficient. If this does not work, try specifying + your system manually (for the Darwin platform + (Apple Mac OS X) specify BSD here). + + Default directory for pdnsd cache (default=/var/ +--with-cachedir=dir cache/pdnsd) + This setting can be changed via config file + settings when pdnsd has been built. + + Number of hash buckets to use (default=1024). The + default should be sufficient for most purposes, but + if you want to store a large number of names in the +--with-hash-buckets=num cache, cache lookups may be faster if the number of + hash buckets is comparable to the number of names + stored in the cache. The number actually used is + the smallest power of two greater or equal to the + number specified here. + + Enable ISDN support + This option will work only on Linux and may cause +--enable-isdn problems with 2.0.x or old 2.2.x kernels. You will + need it for a proper if uptest under Linux for ISDN + ppp devices. + +--disable-ipv4 Disable IPv4 networking support (default=enabled) + + Enable IPv6 networking support. +--enable-ipv6 If your OS does support IPv6 properly, you should + be able to serve also IPv4 queries using this. + Normally, this is disabled and you won't need it. + +--disable-ipv4-startup Disable IPv4 on pdnsd startup by default (default= + enabled) + + Enable IPV6 on pdnsd startup by default (default= + IPv4). These options are only defaults, you can +--enable-ipv6-startup specify on the command line or in the config files + which IP version will really be used. Normally, you + won't need to change these. + +--disable-udp-queries Disable UDP as query method. You shouldn't need to + change this. + + Disable TCP as query method. This only effects the + querying of name servers by pdnsd, not the ability + of pdnsd to answer TCP queries from clients. TCP + queries are slower than UDP queries, but can be +--disable-tcp-queries more secure against certain types of attacks and + are able to handle large answers. For normal use + this can be disabled. (Note that the default has + changed: TCP-query support is now compiled in by + default, but it still depends on the run-time + options whether it is actually used.) + + Specify the query method (default=udponly, others: + tcponly, tcpudp, udptcp). If you have enabled both + UDP and TCP queries, this lets you control which + query method pdnsd will use by default. tcpudp will + try TCP first and fall back to UDP if TCP is not +--with-query-method=qm supported by the server; udptcp will try UDP first + and, if the answer was truncated, will repeat the + query using TCP. udponly and tcponly should be + clear. Note that this only effects the compiled-in + default; the query method can still be changed + using command-line options or options in the + configuration file. + + Disable the TCP server. In this case pdnsd will not +--disable-tcp-server be able to respond to TCP queries from clients. + This may cause problems with very large answers. + + Disable the UDP source address discovery. + You need this only if you have trouble with +--disable-src-addr-disc messages saying "could not discover udp source + address". + For the Cygwin target, this option is disabled by + default. + +--disable-poll Disable poll(2) and use select(2) (default=enabled) + You will normally not need this. + + Since version 1.2.9 this option is obsolete and + ignored. It is now possible to configure for each +--disable-new-rrs RR type separately whether it is cacheable by pdnsd + by editing the file src/rr_types.in. The comments + in this file explain how to do this. + + Enforce strict RFC 2181 compliance. + This will cause pdnsd to reject DNS answers with +--enable-strict-rfc2181 incorrect timestamp settings (multiple RRs of the + same type and for the same domain with different + TTLs). Normally not needed. + + This option is obsolete. Since version 1.2, pdnsd +--enable-underscores places no restrictions on the types of characters + in domain names (there are still a few restrictions + for locally defined names, though). + + Specify random device; default: C Library random() + PRNG + pdnsd uses (pseudo-) random numbers as query IDs + for security reasons (this makes forging DNS + answers more difficult). This option controls where + pdnsd gets these from. The default is the C library + random() function, which is relatively weak. You + can specify a device like /dev/urandom here if you +--with-random-device= like; pdnsd will read random numbers from it +device 16-bit-wise. /dev/urandom is present under Linux + and most BSD derivates. You should not use /dev/ + random - it is more secure, but may block and delay + pdnsd's answers for a long time. + You can specify arc4random to use the BSD + arc4random() library function (default for FreeBSD + target), which is considered safe. + You can also specify random as device to use the C + Library random() function (described above). + + Specify default user for pdnsd (default=nobody). + This is the user that will be entered for the +--with-default-id=user run_as option in the config file (see below) that + will be installed during make install. You can + change this any time in your config file. + + Specify debugging level. Normally you can safely + switch debugging off by setting the level to 0. + This will increase speed (although only marginally) + and save space in the executable (only about 12kB). + However, more significant may be the savings in + stack space, especially if pdnsd is put under heavy + load and there are many simultaneous running + threads. + Presently the only defined debug levels are in the + range 0 - 9. Setting the level to 9 enables hex +--with-debug=level dumps of the queries and replies pdnsd receives and + should normally not be needed. Debug output will + only be generated if you turn on special switches; + it might be useful for debugging your config files, + so I recommend using the default (1). However, if + you use pdnsd under heavy load, a better strategy + may be to compile one version of pdnsd without + debug support (configured with --with-debug=0) for + production use, and one version with with debug + support (e.g. --with-debug=9) for diagnostic + purposes. + +--with-verbosity=level Specify default message verbosity. The default + should be ok. + + Enable RCS IDs in executables (default=disabled). +--enable-rcsids For personal use, there is no need to do this. If + you build rpm's, it might have advantages. + + Enable subsequent tcp queries. The DNS protocol + standard requires that servers must be capable of + answering multiple subsequent queries that are sent + over the same tcp connection, and that the server + may only close the connection by himself after a + certain timeout. This feature is rarely used, but +--enable-tcp-subseq may make denial-of-service attacks easier, as it + allows for an attacker to hold a connection open a + long time (although the attacker's IP is most + likely revealed then). For full standard + compliance, you should use this option. If you do + not use --enable-tcp-server, is option is not + honored. + + Specify default tcp query timeout after which the + connection is closed if no full query has been +--with-tcp-qtimeout=secs received. The default is 30s. You can also change + this option at run time using the tcp_qtimeout + config file option. If you do not use + --enable-tcp-server, is option is not honored. + + Specify the default number of queries that can be + executed in parallel. You can also change this +--with-par-queries=num option at run time using the par_queries config + file option. See the description of that option for + an explanation of what it really does. + The default for this option is 2. + + New in version 1.2.9b: Specify the maximum number + of IP addresses that can be used per nameserver + obtained from NS records (when resolving names + recursively). Just one IP address per nameserver is + sufficient in the vast majority of cases (and this +--with-max-nameserver-ips was the strategy used by pdnsd in previous +=num versions), but in rare cases this will cause + unnecessary resolve failures if the address chosen + for each nameserver happens to be unreachable while + the other addresses would lead to successful + resolution. + The default for this option is 3. + + Added by Paul Rombouts: Use this option if you + experience problems with signal handling under + Linux. The usual symptom is that pdnsd fails to + save the cache to disk, and /var/cache/pdnsd/ + pdnsd.cache remains empty. If you experience this + kind of trouble, try reconfiguring with different + values for the --with-thread-lib option. The + allowable values are linuxthreads (or lt for + short), linuxthreads2 (or lt2 for short), and nptl. + By default the configure script tries to detect +--with-thread-lib=lib automatically whether linuxthreads or nptl is more + appropriate for your system, but the method used is + not foolproof. Look for the line: checking if this + is an NPTL-based system... + If the automatic test mistakenly indentifies the + thread library on your system as NPTL based, you + should reconfigure with --with-thread-lib=lt and + recompile. If the result of the automatic test is + "no" or if --with-thread-lib=lt does not have the + desired effect, try again using --with-thread-lib= + lt2 . + +Normally, you will need only --prefix, --sysconfdir and --with-distribution. If +you specify your distribution using --with-distribution, this has the following +effects: + + * An rc script is copied in the appropriate localtion, which enables pdnsd to + start at machine boot time (see 0.4) + * Distribution-specific portions might be included in the generated + pdnsd.spec file (only important if you want to build rpm archives + yourself). + +If you choose Generic, no rc script is installed, and a generic spec file is +generated. +Further instructions are in the INSTALL document in the pdnsd source directory. +./configure --help will give you a list of all supported command line options. + +Note added by Paul Rombouts: Some people may want change the compiler +optimization flag. I use the -O2 flag, but it might be safer to use a lower +level of optimization or no optimization at all. In that case prefix the +configure command with the desired compiler flags like this (assuming you're +using a bash shell): + +CFLAGS="-O1 -Wall" ./configure ... + + +0.3.3 Building & installing + +Type make in the source directory. Should work by now. +To install, type make install or do the installation by hand (see 0.3.4). +make install will do the following ($prefix is the prefix directory; see +above): + + 1. copies pdnsd to $(prefix)/sbin/ + 2. copies pdnsd-ctl to $(prefix)/sbin/ + 3. copies docs/pdnsd.conf.sample (a sample configuration) to the pdnsd config + directory. + 4. creates your cache directory if it is not there. After installation, you + should check the file permissions and create or edit /etc/pdnsd.conf to fit + your needs (see below). If you use the run_as option, please make sure that + your cache directory is owned by the user you specified with this option! + +You must be root for this installation! +Security notes: never make the pdnsd cache directory writeable for untrusted +users, or you will get several security holes: the users might modify the cache +contents, or plant dangerous links. +If you use a pidfile, you should be aware that you introduce security problems +if you place the pidfile in a directory in a NFS filesystem that is writeable +for untrusted users. Generally, the pidfile directory (typically /var/run) +should not be writeable for untrusted users. + +0.3.4 Manual installation + +For a manual installation, you need to do the following steps: + + 1. Copy pdnsd and pdnsd-ctl from your build directory to an appropriate + location (e.g. /usr/sbin). + 2. Copy docs/pdnsd.conf into the directory you want it to reside (/etc by + default, and change it according to your needs (see below). + 3. Create your caching directory; default is /var/cache/pdnsd (you may change + this in your pdnsd.conf); Permissions should be at max rwxr-xr-x (if you + want to protect your cache and status socket, make it rwx------). + +Thats it! + +0.4 Start at boot time + +In the src/rc folder of the pdnsd distribution are start scripts for pdnsd +designed for different Linux distros. There are scripts for SuSE, Redhat, +Debian, Arch Linux and Slackware now. +The start scripts are automatically installed during RPM install, and also +during make install if you specified your distro. +For Slackware Linux there is a start-up script contributed by Nikola Kotur, but +presently it must be installed manually. See src/rc/README and src/rc/Slackware +/rc.pdnsd for details. + +0.4.1 SuSE Linux startup + +rc/SuSE/pdnsd is a start script for SuSE Linux. It was tested for 6.? but +should run on some versions below. You can do make install as root in the rc/ +SuSE directory to install it, or you can install manually: + +manual installation + +For manual installation, copy rc/SuSE/pdnsd into /sbin/init.d/, go to /sbin/ +init.d/rc2.d/ and create there the following two symlinks: +S11pdnsd to ../pdnsd (do ln -s ../pdnsd S11pdnsd in that dir) +K34pdnsd to ../pdnsd (do ln -s ../pdnsd K34pdnsd in that dir) +The numbers dictate the order different services are started and might need to +be modified. Then edit your /etc/rc.config file and add the line START_PDNSD= +yes to start pdnsd at boot time. + +If you used the make install command, START_PDNSD=yes has been appended to your +/etc/rc.config file, causing pdnsd to be started at boot time. If you don't +want that, change the yes into no. + +This start script was created from /sbin/init.d/skeleton by me, so the most is +copyrighted by SuSE. They put it under the GPL, however, so the license stated +in COPYING also applies to this script. There is NO WARRANTY OF ANY KIND on +these scripts. This is no official SuSE script, and SuSE naturally does NO +support for it. + +0.4.2 Red Hat Linux startup + +rc/Redhat/pdnsd is a start script for Red Hat Linux. It was contibuted by +Torben Janssen. +This was tested for 6.1 but should run on 5.0+. You can do make install as root +in the rc/Redhat directory to install it, or you can install manually: + +manual installation + +For manual installation, copy rc/Redhat/pdnsd into /etc/rc.d/init.d/ +Then go to /etc/rc.d/rc3.d and create there the following symlink: +S78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd S78pdnsd in that dir) +Then go to /etc/rc.d/rc0.d and create there the following symlink: +K78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) +Then go to /etc/rc.d/rc6.d and create there the following symlink: +K78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) + +This script is also covered by license stated in COPYING. Again, there is NO +WARRANTY OF ANY KIND on these scripts. This is no offical Redhat script, and +Redhat naturally does NO support for it + +0.5 Notes for FreeBSD users + +The special handling of ISDN ppp devices is only supported on Linux. It is not +needed in FreeBSD, the normal device handling also works fine with isdn4bsd +devices. +When compiled for FreeBSD, pdnsd as a small RFC compatability issue: RFC2181 +demands answers on dns querys to be sent with the same source address the query +packet went to. In seldom cases, this will not be the case, because the kernel +selects the source address depending on the interface that was used for sending +the answer. +Setting the source address currently does not work for IPv4. I have written a +kernel patch that will provide an easy way to program this. We'll see if or +when it gets commited. + + + +1 Invocation + +When invoking pdnsd, you can specify various options at the command line. +Command line options always override config file options. The various --noX +options are present to override config file options. + +pdnsd --help (or -h) gives you an overview of the pdnsd command line options. + +pdnsd --version (or -V for short) prints licence and version information. + +To start pdnsd as background daemon, specifiy --daemon (or -d for short) on the +command line. Diagnostic and error messages after the actual daemon start will +be printed to the syslog instead of the console. --nodaemon will disable this. + +When starting pdnsd as a daemon, the -p option may be helpful: It writes the +pid of the server process to the file of the name given as argument to this +option. +Example: pdnsd -d -p /var/run/pdnsd.pid + +If you want to specify a configuration file other than /etc/pdnsd.conf, specify +-c or --config-file on the command line, followed by a filename. + +If pdnsd was compiled with debugging support, you may specify -g or --debug on +the command line. This will cause extra diagnostic messages to be printed. When +pdnsd runs in daemon mode, the messages will be written to the pdnsd.debug file +in your cache directory. --nodebug disables debugging. + +pdnsd -vn sets the verbosity level of pdnsd. n is normally a digit from 0 to 3, +where 0 means normal operation, while 3 will most verbose. Level 9 can be used +in combination with the --debug option for very extensive debug information. +Note: The current implementation mostly ignores the verbosity level, so you may +not notice much difference between the various levels. + +The option -s or --status enables the status control socket. This is a named +socket in the cache directory called pdnsd.status. This socket allows run-time +configuration of pdnsd using the utility pdnsd-ctl. See below for more details +about pdnsd-ctl. --nostatus disables status control. See also the configuration +option status_ctl in the global section. + +The option --notcp disables the seldom needed TCP server thread, which may save +you some resources. -t or --tcp will enable it. See also the tcp_server +configuration option. + +Using the -m option, you can select the method pdnsd uses to query other name +servers. Following methods are supported (see also the query_method +configuration option): +-muo: pdnsd will use UDP only. This is the fastest method, and should be +supported by all name servers on the Internet. +-mto: pdnsd will use TCP only. TCP queries usually take longer time than UDP +queries, but are more secure against certain attacks, where an attacker tries +to guess your query id and to send forged answers. TCP queries are not +supported by some name servers. +-mtu: pdnsd will try to use TCP, and will fall back to UDP if its connection is +refused or times out. +-mut: New in version 1.2.5: pdnsd will try to use UDP, and will repeat the +query using TCP if the UDP reply was truncated (i.e. the tc bit is set). This +is the behaviour recommended by the DNS standards. + +The -4 option switches to IPv4 mode, providing pdnsd was compiled with IPv4 +support. +The -6 option switches to IPv6 mode, providing pdnsd was compiled with IPv6 +support. +The -a option is only available when pdnsd was compiled with both IPv4 and IPv6 +support. With this option, pdnsd will try to detect automatically if a system +supports IPv6, and fall back to IPv4 otherwise. + +With -i prefix or --ipv4_6_prefix=prefix you can set the prefix pdnsd uses +(when running in IPv6 mode) to map IPv4 addresses in the configuration file to +IPv6 addresses. There is also a corresponding option for the config file, see +below. Must be a valid IPv6 address. The default is ::ffff:0.0.0.0 + +2 The configuration file + +This section describes the layout of the configuration file and the available +configuration options. The default location of the file is /etc/pdnsd.conf. +This may be changed with the -c command line option. An example pdnsd.conf +comes with the pdnsd distribution in the docs directory and will be installed +to /etc/ by make install. + +2.1 Layout + +The configuration file is divided into sections. Each section is prefixed with +the section name and opening curlies ({) and closed with closing curlies (}). +In each section, configuration options can be given in the form +option_name=option_value; +Option value may be a string literal, a number, a time specification or a +constant. In previous versions of pdnsd strings had to be enclosed in quotes +("), but since version 1.1.10 this is no longer necessary, unless a string +contains a special character such as whitespace, a token that normally starts a +comment, or one of ",;{}\". Since version 1.2.9 a backslash (\) inside a string +is interpreted as an escape character, so it is possible to include special +characters in strings (both quoted or unquoted) by preceding them with a +backslash. Some escape sequences are in interpreted as in the C programming +language, e.g. \t becomes a tab, \n becomes a new-line control char. +A time specification consists a sequence of digits followed by a one-letter +suffix. The following suffixes are recognized: s (seconds), m (minutes), h +(hours), d (days) and w (weeks). If the suffix is missing, seconds are assumed. +If several time specifications are concatenated, their values are added +together; e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. +Some options take more than one value; in this case, the values are separated +with commas. +If you may supply one of a set of possible values to an option, this is noted +in the documentation as (option1|option2|option3|...) +The constants true|false and yes|no are accepted as synonyms for the constants +on|off. +Comments may be enclosed in /* and */, nested comments are possible. If the # +sign or two slashes (//) appear in the configuration file, everything from +these signs to the end of the current line is regarded as a comment and +ignored. +There are examples for nearly all options in the sample config file. + +2.1.1 global Section + +The global section specifies parameters that affect the overall behaviour of +the server. If you specify multiple global sections, the settings of those +later in the file will overwrite the earlier given values. +These are the possible options: + + +perm_cache=(number|off); +Switch the disk cache off or supply a maximum cache size in kB. If the disk +cache is switched off, 8 bytes will still be written to disk. The memory cache +is always 10kB larger than the file cache. This value is 2048 (2 MB) by +default. + +cache_dir=string; +Set the directory you want to keep the cache in. The default is "/var/cache/ +pdnsd" (unless pdnsd was compiled with a different default). + +server_port=number; +Set the server port. This is especially useful when you want to start the +server and are not root. Note that you may also not specify uptest=ping in the +server section as non-root. +The default port is 53, the RFC-standard one. Note that you should only use +non-standard ports when you only need clients on your machine to communicate +with the server; others will probably fail if the try to contact the server on +the basis of an NS record, since the A record that supplies the address for +(among others) name servers does not have a port number specification. + +server_ip=string; +or +interface=string; +Set the IP address pdnsd listens on for requests. This can be useful when the +host has several interfaces and you want pdnsd not to listen on all interfaces. +For example, it is possible to bind pdnsd to listen on 127.0.0.2 to allow pdnsd +to be a forwarder for BIND. The default setting for this option is server_ip= +any, which means that pdnsd will listen on all of your local interfaces. +Presently you can only specify one address here; if you want pdnsd to listen on +multiple interfaces but not all you will have to specify server_ip=any and use +firewall rules to restrict access. +The IP address used to need quotation marks around it, but since version 1.1.10 +this is no longer necessary. +If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to +specify an IPv6 address here, then unless pdnsd was compiled to start up in +IPv6 mode by default, you will need to use the -6 command-line option or set +run_ipv4=off first (see below) in order to ensure that the IPv6 address is +parsed correctly. +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, it will +automatically be mapped to an IPv6 address. +New in version 1.2: You may also give the name of an interface such as "lo" or +"eth0" here, instead of an IP address (this has been tested on Linux, and may +or may not work on other platforms). pdnsd will not bind to the interface name, +but will look up the address of the interface at start-up and listen on that +address. If the address of the interface changes while pdnsd is running, pdnsd +will not notice that. You will need to restart pdnsd in that case. + +outgoing_ip=string; +or +outside_interface=string; +New in version 1.2.9: Set the IP address of the interface used by pdnsd for +outgoing queries. This can be useful when the host has several interfaces and +you want pdnsd to send outgoing queries via only one of them. For example, if +pdnsd is running on a host with one interface with IP address 192.168.1.1 +connected to the local network, and another with IP address 123.xxx.yyy.zzz +connected to the internet, you may specify server_ip=192.168.1.1 and +outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds to queries +received from the local network, and only sends outgoing queries via the +interface connected to the internet. +The default setting for this option is any, which means that the kernel is free +to decide which interface to use. Like with the server_ip option, you may also +give the name of an interface here, instead of an IP address. + +linkdown_kluge=(on|off); +This option enables a kluge that some people might need: when all servers are +marked down, with this option set the cache is not even used when a query is +received, and a DNS error is returned in any case. The only exception from this +is that local records (as specified in rr and source sections are still served +normally. In general, you probably want to get cached entries even when the +network is down, so this defaults to off. + +max_ttl=timespec; +This option sets the maximum time a record is held in cache. All dns resource +records have a time to live field that says for what period of time the record +may be cached before it needs to be requeried. If this is more than the value +given with max_ttl, this time to live value is set to max_ttl. This is done to +prevent records from being cached an inappropriate long period of time, because +that is almost never a good thing to do. Default is 604800s (one week). + +min_ttl=timespec; +This option sets the minimum time a record is held in cache. All dns resource +records have a time to live field that says for what period of time the record +may be cached before it needs to be requeried. If this is less than the value +given with min_ttl, this time to live value is set to min_ttl. Default is 120 +seconds. + +neg_ttl=timespec; +This option sets the time that negatively cached records will remain valid in +the cache if no time to live can be determined. This is always the case when +whole domains are being cached negatively, and additionally when record types +are cached negatively for a domain for which no SOA record is known to pdnsd. +If a SOA is present, the ttl of the SOA is taken. + +neg_rrs_pol=(on|off|auth|default); +This sets the RR set policy for negative caching; this tells pdnsd under which +circumstances it should cache a record type negatively for a certain domain. +off will turn the negative caching of record types off, on will always add a +negative cache entry when a name server did not return a record type we asked +it for, and auth will only add such entries if the answer came from an +authoritative name server for that domain. +New in version 1.2.8: The default setting will add a negatively cached record +if either the answer was authoritive or the answer indicated the name server +had "recursion available" while the query explicitly requested such recursion. +The preset is "default" (used to be auth). + +neg_domain_pol=(on|off|auth); +This is analogue to neg_rrs_pol for whole domain negative caching. It should be +safe to set this on, because I have not seen a caching server that will falsely +claim that a domain does not exist. +The default is auth. + +run_as=string; +This option allows you to let pdnsd change its user and group id after +operations that needed privileges have been done. This helps minimize security +risks and is therefore recommended. The supplied string gives a user name whose +user id and primary group id are taken. +A little more details: after reading the config file, becoming a daemon (if +specified) and starting the server status thread, the main thread changes its +gid and uid, as do all newly created threads thereafter. By taking another uid +and gid, those threads run with the privileges of the specified user. Under +Linux and FreeBSD, the server status thread runs with the original privileges +only when the strict_setuid option is set to off (see below, on by default), +because these may be needed for exec uptests. The manager thread also retains +its original privileges in this case. You should take care that the user you +specify has write permissions on your cache file and status pipe (if you need a +status pipe). You should look out for error messages like "permission denied" +and "operation not permitted" to discover permission problems. + +strict_setuid=(on|off); +When used together with the run_as option, this option lets you specify that +all threads of the program will run with the privileges of the run_as user. +This provides higher security than the normal run_as option, but is not always +possible. See the run_as option for further discussion. +This option is on by default. +Note that this option has no effect on Non-Linux systems. + +paranoid=(on|off); +Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers +to query other servers themselves if possible, and to give back answers for +domains that may not be in its authority), and accepts additional records with +information for servers that are not in the authority of the queried server. +This opens the possibility of so-called cache poisoning: a malicious attacker +might set up a dns server that, when queried, returns forged additional +records. This way, he might replace trusted servers with his own ones by making +your dns server return bad IP addresses. This option protects you from cache +poisoning by rejecting additional records that do not describe domains in the +queried servers authority space and not doing recursive queries any more. An +exception to this rule are the servers you specify in your config file, which +are trusted. +The penalty is a possible performance decrease, in particular, more queries +might be necessary for the same operation. +You should also notice that there may be other similar security problems, which +are essentially problems of the DNS, i.e. any "traditional" server has them +(the DNS security extensions solve these problems, but are not widely +supported). One of this vulnerabilities is that an attacker may bombard you +with forged answers in hopes that one may match a query you have done. If you +have done such a query, one in 65536 forged packets will be succesful (i.e. an +average packet count of 32768 is needed for that attack). pdnsd can use TCP for +queries, which has a slightly higher overhead, but is much less vulnerable to +such attacks on sane operating systems. Also, pdnsd chooses random query ids, +so that an attacker cannot take a shortcut. If the attacker is able to listen +to your network traffic, this attack is relatively easy, though. +This vulnerability is not pdnsd's fault, and is possible using any conventional +name server (pdnsd is perhaps a little more secured against this type of +attacks if you make it use TCP). +The paranoid option is off by default. + +ignore_cd=(on|off); +New in version 1.2.8: This option lets you specify that the CD bit of a DNS +query will be ignored. Otherwise pdnsd will reply FORMERR to clients that set +this bit in a query. It is safe to enable this option, as the CD bit refers to +'Checking Disabled' which means that the client will accept non-authenticated +data. +This option is on by default. Turn it off if you want the old behaviour (before +version 1.2.8). + +scheme_file=string; +In addition to normal uptests, you may specify that some servers shall only be +queried when a certain pcmcia-cs scheme is active (only under linux). For that, +pdnsd needs to know where the file resides that holds the pcmcia scheme +information. Normally, this is either /var/lib/pcmcia/scheme or /var/state/ +pcmcia/scheme. + +status_ctl=(on|off); +This has the same effect as the -s command line option: the status control is +enabled when on is specified. +Added by Paul Rombouts: Note that pdnsd-ctl allows run-time configuration of +pdnsd, even the IP addesses of the name servers can be changed. If you're not +using pdnsd-ctl and you want maximum security, you should not enable this +option. It is disabled by default. + +daemon=(on|off); +This has the same effect as the -d command line option: the daemon mode is +enabled when on is specified. +Default is off. + +tcp_server=(on|off); +tcp_server=on has the same effect as the -t or --tcp command-line option: it +enables TCP serving. Similarly, tcp_server=off is like the --notcp command-line +option. +Default is on. + +pid_file=string; +This has the same effect as the -p command line option: you can specify a file +that pdnsd will write its pid into when it starts in daemon mode. + +verbosity=number; +This has the same effect as the -v command line option: you can set the +verbosity of pdnsd's messages with it. The argument is a number between 0 (few +messages) to 3 (most messages). + +query_method=(tcp_only|udp_only|tcp_udp|udp_tcp); +This has the same effect as the -m command line option. Read the documentation +for the command line option on this. tcp_only corresponds to the to, udp_only +to the uo, tcp_udp to the tu and udp_tcp to the ut argument of the command line +option. +If you use query_method=tcp_udp, it is recommended that you also set the global +timeout option to at least twice the longest server timeout. + +run_ipv4=(on|off); +This has the same effect as the -4 or -6 command line option: if on is +specified, IPv4 support is enabled, and IPv6 support is disabled (if +available). If off is specified, IPv4 will be disabled and IPv6 will be +enabled. For this option to be meaningful, pdnsd needs to be compiled with +support for the protocol you choose. If pdnsd was compiled with both IPv4 and +IPv6 support, and you want to include IPv6 addresses in the configuration file, +you will probably need to specify run_ipv4=off first to ensure that the IPv6 +addresses are parsed correctly. + +debug=(on|off); +This has the same effect as the -g command line option: the debugging messages +are enabled when on is specified. + +ctl_perms=number; +This option allows you to set the file permissions that the pdnsd status +control socket will have. These are the same as file permissions. The owner of +the file will be the run_as user, or, if none is specified, the user who +started pdnsd. If you want to specify the permissions in octal (as usual), +don't forget the leading zero (0600 instead of 600!). To use the status +control, write access is needed. The default is 0600 (only the owner may read +or write). +Please note that the socket is kept in the cache directory, and that the cache +directory permissions might also need to be adjusted. Please ensure that the +cache directory is not writeable for untrusted users. + +proc_limit=number; +With this option, you can set a limit on the pdnsd threads that will be active +simultaneously. If this number is exceeded, queries are queued and may be +delayed some time. See also the procq_limit option. +The default for this option is 40. + +procq_limit=number; +When the query thread limit proc_limit is exceeded, connection attempts to +pdnsd will be queued. With this option, you can set the maximum queue length. +If this length is also exceeded, the incoming queries will be dropped. That +means that tcp connections will be closed and udp queries will just be dropped, +which will probably cause the querying resolver to wait for an answer until it +times out. +See also the proc_limit option. A maximum of proc_limit+procq_limit query +threads will exist at any one time (plus 3 to 6 threads that will always be +present depending on your configuration). +The default for this option is 60. + +tcp_qtimeout=timespec; +This option sets a timeout for tcp queries. If no full query has been received +on a tcp connection after that time has passed, the connection will be closed. +The default is set using the --with-tcp-qtimeout option to configure. + +par_queries=number; +This option used to set the maximum number of remote servers that would be +queried simultaneously, for every query that pdnsd receives. +Since version 1.1.11, the meaning of this option has changed slightly. It is +now the increment with which the number of parallel queries is increased when +the previous set of servers has timed out. For example, if we have a list +server1, server2, server3, etc. of available servers and par_queries=2, then +pdnsd will first send queries to server1 and server2, and listen for responses +from these servers. +If these servers do not send a reply within their timeout period, pdnsd will +send additional queries to server3 and server4, and listen for responses from +server1, server2, server3 and server4, and so on until a useful reply is +received or the list is exhausted. +In the worst case there will be pending queries to all the servers in the list +of available servers. We may be using more system resources this way (but only +if the first servers in the list are slow or unresponsive), but the advantage +is that we have a greater chance of catching a reply. After all, if we wait +longer anyway, why not for more servers. +See also the explanation of the global timeout option below. +1 or 2 are good values for this option. The default is set at compile time +using the --with-par-queries option to configure. + +timeout=timespec; +This is the global timeout parameter for dns queries. This specifies the +minimum period of time pdnsd will wait after sending the first query to a +remote server before giving up without having received a reply. The timeout +options in the configuration file are now only minimum timeout intervals. +Setting the global timeout option makes it possible to specify quite short +timeout intervals in the server sections (see below). This will have the effect +that pdnsd will start querying additional servers fairly quickly if the first +servers are slow to respond (but will still continue to listen for responses +from the first ones). This may allow pdnsd to get an answer more quickly in +certain situations. +If you use query_method=tcp_udp it is recommended that you make the global +timeout at least twice as large as the largest server timeout, otherwise pdnsd +may not have time to try a UDP query if a TCP connection times out. +Default value is 0. + +randomize_recs=(on|off); +If this option is turned on, pdnsd will randomly reorder the cached records of +one type when creating an answer. This supports round-robin DNS schemes and +increases fail safety for hosts with multiple IP addresses, so this is usually +a good idea. +On by default. + +query_port_start=(number|none); +If a number is given, this defines the start of the port range used for queries +of pdnsd. The value given must be >= 1024. The purpose of this option is to aid +certain firewall configurations that are based on the source port. Please keep +in mind that another application may bind a port in that range, so a stateful +firewall using target port and/or process uid may be more effective. In case a +query start port is given pdnsd uses this port as the first port of a specified +port range (see query_port_end) used for queries. pdnsd will try to randomly +select a free port from this range as local port for the query. +To ensure that there are enough ports for pdnsd to use, the range between +query_port_start and query_port_end should be adjusted to at least (par_queries +* proc_limit). A larger range is highly recommended for security reasons, and +also because other applications may allocate ports in that range. If possible, +this range should be kept out of the space that other applications usually use. +The default for this option is 1024. Together with the default value of +query_port_end, this makes it the hardest for an attacker to guess the source +port used by the pdnsd resolver. If you specify none here, pdnsd will let the +kernel choose the source port, but this may leave pdnsd more vulnerable to an +attack. + +query_port_end=number; +Used if query_port_start is not none. Defines the last port of the range +started by query_port_start used for querys by pdnsd. The default is 65535, +which is also the maximum legal value for this option. For details see the +description of query_port_start. + +delegation_only=string; +Added by Paul Rombouts: This option specifies a "delegation-only" zone. This +means that if pdnsd receives a query for a name that is in a subdomain of a +"delegation-only" zone but the remote name server returns an answer with an +authority section lacking any NS RRs for subdomains of that zone, pdnsd will +answer NXDOMAIN (unknown domain). This feature can be used for undoing the +undesired effects of DNS "wildcards". Several "delegation-only" zones may be +specified together. If you specify root servers in a server section it is +important that you set root_server=on in such a section. +Example: + +delegation_only="com","net"; + +This feature is off by default. It is recommended that you only use this +feature if you actually need it, because there is a risk that some legitimate +names will be blocked, especially if the remote name servers queried by pdnsd +return answers with empty authority sections. + +ipv4_6_prefix=string; +This option has the same effect as the -i command-line option. When pdnsd runs +in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 +addresses in the configuration file (or addresses specified with pdnsd-ctl) to +IPv6-mapped addresses. The string must be a valid IPv6 address. Only the first +96 bits are used. Note that this only effects the parsing of IPv4 addresses +listed after this option. +The default is "::ffff.0.0.0.0". + +use_nss=(on|off); +If this option is turned on, pdnsd will call initgroups() to set up the group +access list, whenever pdnsd changes its user and group id (see run_as option). +There is a possible snag, though, if initgroups() uses NSS (Name Service +Switch) and NSS in turn uses DNS. In such a case you may experience lengthy +timeouts and stalls. By setting use_nss=off, you can disable the initgroups() +call (only possible in versions 1.2.5 and later). +This option was contributed by Jan-Marek Glogowski. +On by default. + +udpbufsize=number; +New in version 1.2.9: This option sets the upper limit on the size of UDP DNS +messages. The default is 1024. +See also the edns_query server option below. + + +2.1.2 server Section + +Each server section specifies a set of name servers that pdnsd should try to +get resource records or authoritative name server information from. The servers +are queried in the order of their appearance (or parallel to a limited extend). +If one fails, the next one is taken and so on. +You probably want to specify the dns server in your LAN, the caching dns +servers of your internet provider or even a list of root servers in one or more +server sections. +The supported options in this section are: + + +label=string; +Specify a label for the server section. This can be used to refer to this +section when using pdnsd-ctl, the pdnsd control utility. +You can give several server sections the same label, but if you want to change +the addresses of a server section (see ip option below) during run-time with +"pdnsd-ctl server label up dns1,dns2,...", the label must be unique. + +ip=string; +Give the IP (the address, not the host name) of the server. +Multiple IP addresses can be given per server section. This can be done by +entering multiple lines of the form ip=string; or a single line like this: + +ip=string,string,string; + +IP addresses do not have to be specified in the configuration file. A server +section without IP addresses will remain inactive until it is assigned one or +more addresses with pdnsd-ctl, the pdnsd control utility. +If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses +you specify here will be skipped with a warning message, unless pdnsd is +running in IPv6 mode. Thus, unless pdnsd was compiled to startup in IPv6 mode +by default, you need to use the command-line option -6 or set run_ipv4=off +first (see global section) in order to ensure that IPv6 addresses are parsed +correctly. +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, it will +automatically be mapped to an IPv6 address. + +file=string; +New in version 1.2: This option allows you to give the name of a +resolv.conf-style file. Of the lines beginning with the nameserver keyword, the +second field will be parsed as an IP address, as if it were specified with the +ip= option. The remaining lines will be ignored. If the contents of the file +changes while pdnsd is running, you can make pdnsd aware of the changes through +the use of pdnsd-ctl, the pdnsd control utility. This is usually most +conveniently done by placing the command "pdnsd-ctl config" in a script that is +automatically run whenever the DNS configuration changes. +For example, suppose you have a ppp client that writes the DNS configuration +for your ISP to the file /etc/ppp/resolv.conf and runs the script /etc/ppp/ +ip-up when a new connection is established. One way of ensuring that pdnsd is +automatically reconfigured is to add a server section in the config file with +file=/etc/ppp/resolv.conf and to add the command "pdnsd-ctl config" to /etc/ppp +/ip-up. + +port=number; +Give the port the remote name server listens on. Default is 53 (the official +dns port) + +uptest=(ping|none|if|dev|diald|exec|query); +Determine the method to check whether the server is available. Currently +defined methods are: + + * ping: Send an ICMP_ECHO request to the server. If it doesn't respond within + the timeout, it is regarded to be unavailable until the next probe. + * none: The availability status is not changed, only the time stamp is + updated. + * if: Check whether the interface (specified in the interface= option) is + existent, up and running. This currently works for all "ordinary" network + interfaces, interfaces that disappear when down (e.g. ppp?), and + additionally for Linux isdn interfaces (as of kernel 2.2). Note that you + need a /dev/isdninfo device file (major#45, minor#255), or the isdn uptest + will always fail. + * dev and diald: Perform an if uptest, and, if that was succesful, + additionally check whether a program is running that has locked a given + (modem-) device. The needed parameters are an interface (specified as for + the if uptest, e.g. "ppp0") and a device relative to /dev (e.g. "modem" for + /dev/modem specified using the device= option. pdnsd will then look for a + pid file for the given interface in /var/lock (e.g. /var/run/ppp0.pid) and + for a lockfile for the given device (e.g. /var/lock/LCK..modem), and then + test whether the locking process is the process that created the pid file + and this process is still alive. If this is the case, the normal if uptest + is executed for the given interface. + The dev option is for pppd dial-on-demand, diald is the same for diald + users. + * exec: Executes a given command in the /bin/sh shell (as /bin/sh -c + ) and evaluates the result (the return code of the last command) + in the shell's way of handling return codes, i.e. 0 indicates success, all + other indicate failure. The shell's process name will be uptest_sh. The + command is given with the uptest_cmd option (see below). For secuity + issues, also see that entry. + * query: New in version 1.2: This works like the ping test, except it sends + an (empty) DNS query to the remote server. If the server sends a + well-formed response back within the timeout period (except SERVFAIL), it + will be regarded as available. This test is useful if a remote server does + not respond to ICMP_ECHO requests at all, which unfortunately is quite + common these days. It can also happen that a remote server is online but + ignores empty DNS queries. Then you will need the set the query_test_name + option (see below). In many cases this test will be a more reliable + indicator of availability than the ones mentioned before. + +The default value is none. + +NOTE: If you use on-demand dialing, use none, if, dev, diald or exec, since +ping or query will send packets in the specified interval and the interface +will thus frequently dial! + +ping_timeout=number; +Sets the timeout for the ping test in tenths of seconds (this unit is used for +legacy reasons; actually the current implementation is only accurate to a +second). +The default is 600 (one minute). + +ping_ip=string; +The IP address for the ping test. The default is the IP of the name server. + +query_test_name=string; +New in version 1.2.9: Sets the name to be queried when using uptest=query +availability test. If the string is the unquoted constant none, an empty query +is used (this the default), otherwise a query of type A will be sent for the +domain name specified here. It is not necessary for the domain name to exist or +have a record of type A in order for the uptest to succeed. +If the the remote server ignores empty queries, you will probably want to set +query_test_name="." (the root domain). + +uptest_cmd=string,string; +or +uptest_cmd=string; +Sets the command for the uptest=exec function to the first string. If the +second string is given, it specifies a user with whose user id and primary +group id the command is executed. +This is especially useful if you are executing the server as root, but do not +want the uptest to be performed with root privileges. In fact, you should never +execute the uptest as root if you can help it. +If the server is running setuid or setgid, the privileges thus gained are +attempted to be dropped even before changing identity to the specified user to +prevent setuid/gid security holes (otherwise, any user might execute commands +as root if you setuid the executable). +Note that this is not always possible, and that pdnsd should never be installed +as setuid or setgid. The command is executed using /bin/sh, so you should be +able to use shell builtin commands. + +interval=(timespec|onquery|ontimeout); +Sets the interval for the server up-test. The default is 900 seconds; however, +a test is forced when a query times out and the timestamp is reset then. +If you specify onquery instead of a timeout, the interface will be tested +before every query. This is to prevent automatically dialing interfaces (diald/ +pppd or ippp) to dial on dns queries. It is intended to be used in connection +with an interface-testing uptest ;-) +Note that using uptest=exec, you might run into performance problems on slow +machines when you use that option. DON'T use onquery with uptest=ping or uptest +=query, as it may cause delays if the server does not answer (btw, it doesn't +make sense anyway). Note also that using onquery is no guarantee that the +interface will not be used. When another (reachable) dns server tells pdnsd to +query a third dns server for data, pdnsd will do that and has no means of +checking whether this will dial up the interface or not. This however should be +a rare situation. +New in version 1.2.3: A third possibility is to specify interval=ontimeout. In +this case the server is not tested at startup/reconfiguration, nor at regular +intervals, but only after a DNS query to a server times out. Certain types of +network problems such as a refused connection will also cause the server to be +considered unavailable. However, once a server is declared dead it is never +considered again unless it is revived using a pdnsd-ctl config or server +command. The idea behind this option is to minimize uptests by assuming all +servers are available until there is reason to believe otherwise. + +interface=string; +The network interface (or network device, e.g. "eth0") for the uptest=if +option. Must be specified if uptest=if is given. + +device=string; +The (modem-) device that is used for the dev uptest. If you use this for a +dial-on-demand ppp uptest (together with uptest=dev), you need to enter the +device you are using for your pppd here, e.g. modem for /dev/modem. +Must be specified if uptest=dev is given. + +timeout=timespec; +Set the timeout for the dns query. The default is 120 seconds. You probably +want to set this lower. +Timeouts specified in the configuration file are only treated as the minimum +period of time to wait for a reply. A queries to a remote server are not +canceled until a useful reply has been received, or all the other queries have +timed out or failed. +If you have also set the global timeout option, you may consider setting a +fairly small value here. See the explanation of the timeout option in the +global section for what that means. + +purge_cache=(on|off); +In every fetched dns record, there is a cache timeout given, which specifies +how long the fetched data may be cached until it needs to be reloaded. If +purge_cache is set to off, the stale records are not purged (unless the cache +size would be exceeded, in this case the oldest records are purged). Instead, +they are still served if they cannot succesfully be updated (e.g. because all +servers are down). +Default is off. + +caching=(on|off); +Specifies if caching shall be performed for this server at all. Default is on. + +lean_query=(on|off); +Specifies whether to use the "lean" query mode. In this mode, only the +information actually queried from pdnsd is resolved and cached. This has the +advantage that usually less cache space is used and the query is usually +faster. In 90% of the cases, only address (A) records are needed anyway. If +switched off, pdnsd will always cache all data about a host it can find and +will specifically ask for all available records (well, at least it is a good +approximation for what it really does ;-) This will of course increase the +answer packet sizes. +Some buggy name servers may not deliver CNAME records when not asked for all +records. I do not know if such servers are around, but if you have trouble +resolving certain host names, try turning this option off. +A last note: If you use multiple pdnsd's that access each other, turning this +option on is probably a big win. +This on by default. + +edns_query=(on|off); +New in version 1.2.9: Specifies whether to use EDNS (Extension mechanisms for +DNS) for outgoing queries. Currently this is only useful for allowing UDP +message sizes larger than 512 bytes. Note that setting this option on can give +problems in combination with some legacy systems or software, including, +embarrassingly enough, previous versions of pdnsd. +The default is off, but if your network can handle UDP payloads significantly +larger than 512 bytes, the recommended value is on. +Note that this option only effects outgoing queries. If pdnsd receives a query +using EDNS, it will reply using EDNS regardless of the value of this option. +See also the udpbufsize option above. + +scheme=string; +You can specify a pcmcia-cs scheme that is used in addition to the uptests. If +you specify a scheme here, the server this section is for will only be queries +if the given scheme is active. Shell wildcards (* and ?) are allowed in the +string under their special meanings. You need to use the scheme_file option on +the global section to make this option work. + +preset=(on|off); +This allows you to specify the initial state of a server before any uptest is +performed. on specifies that the server is regarded available. The default is +on. This is especially useful when you set uptest=none; and want to change the +status of a server only via pdnsd-ctl. + +proxy_only=(on|off); +When this option is set to on, answers given by the servers are always +accepted, and no other servers (as, for example, specified in the NS records of +the query domain) are queried. If you do not turn this option on, pdnsd will do +such queries in some cases (in particular when processing ANY queries). +This option is useful when you do not want pdnsd to make connections to outside +servers for some reasons (e.g. when a firewall is blocking such queries). +I recommend that you turn on lean_query when using this option. +Default is off. + +root_server=(on|off|discover); +Set this option to on if the servers specified in a section are root servers. A +root server will typically only give the name servers for the top-level domain +in its reply. Setting root_server=on will cause pdnsd to try to use cached +information about top-level domains to reduce to number of queries to root +servers, making the resolving of new names more efficient. You can get a list +of available root servers by running the command "dig . ns". +This option is also necessary if you use the delegation_only option. +New in version 1.2.8: This option may also be set to "discover". This will +cause pdnsd to query the servers provided with the ip= option to obtain the +full list of root servers. The root-server addresses will replace the addresses +specified with the ip= option. This will only be done once on startup, or after +a "pdnsd-ctl config" command. In this case the name servers specified with the +ip= option don't have to be root servers, they just have to know the names and +addresses of the root servers. After root-server discovery pdnsd will behave +just as if root_server=on had been specified. +Default is off. + +randomize_servers=(on|off); +New in version 1.2.6: Set this option to on to give each name server in this +section an equal chance of being queried. If this option is off, the name +servers are always queried starting with the first one specified. Even with +this option on, the query order is not truly random. Only the first server is +selected randomly; the following ones are queried in consecutive order, +wrapping around to the beginning of the list when the end is reached. Note that +this option only effects the order within a section. The servers in the first +(active) section are always queried before those in the second one, etc. +The default is off, but if you are resolving from root servers setting this +option on is highly recommended. If root_server=on this option also effects the +query order of the name servers for the top-level domains. + +reject=string; +New in version 1.2.6: This option can be used to make pdnsd reject replies that +contain certain IP addresses. You can specify a single IP address, which will +be matched exactly, or a range of addresses using an address/mask pair. The +mask can be specified as a simple integer, indicating the number of initial 1 +bits in the mask, or in the usual IP address notation. IP addresses may be +either IPv4 or IPv6 (provided there is sufficient support in the C libraries +and support for AAAA records was not disabled). When addresses in the reject +list are compared with those in a reply, only the bits corresponding to those +set in the netmask are significant, the rest are ignored. +Multiple addresses or address/mask pairs may be specified; this can be done by +entering multiple lines of the form reject=string; or a single line like this: + +reject=string,string,string; + +How pdnsd reacts when an address in the reply matches one in the reject list, +depends on the reject_policy option, see below. + +reject_policy=(fail|negate); +New in version 1.2.6: This option determines what pdnsd does when an address in +the reply from a name server matches the reject list (see above). If this +option is set to fail, pdnsd will try another server, or, if there no more +servers to try, return the answer SERVFAIL. If this option is set to negate, +pdnsd will immediately return the answer NXDOMAIN (unknown domain) without +querying additional servers. The fail setting is useful if you don't always +trust the servers in this section, but do trust the servers in the following +section. The negate setting can be used to completely censor certain IP +addresses. In this case you should put the same reject list in every server +section, and also set the reject_recursively option (see below) to true. +The default is fail. + +reject_recursively=(on|off); +New in version 1.2.6: Normally pdnsd checks for addresses in the reject list +(see above) only when the reply comes directly from a name server listed in the +configuration file. With this option set to on, pdnsd will also do this check +for name servers that where obtained from NS records in the authority section +of a previous reply (which was incomplete and non-authoritative). +Default is off. + +policy=(included|excluded|simple_only|fqdn_only); +pdnsd supports inclusion/exclusion lists for server sections: with include= and +exclude= (see below) you can specify domain names for which this server will be +used or will not be used. The first match counts (i.e., the first include or +exclude rule in a server section that matches a domain name is applied, and the +search for other rules is terminated). If no rule matched a given domain name, +the policy= option determines whether this server is used for the lookup for +that domain name; when included is given, the server will be asked, and when +excluded is given, it will not. If simple_only is given the server will be used +if the name to lookup is a simple (single-label) domain name, on the other hand +if fqdn_only is given the server will be used only for names consisting of two +or more labels (i.e. the name has at least one dot in-between). +If no server is available for a queried domain, pdnsd will return an error +message to the client that usually will stop the client's attempts to resolve a +specific domain from this server (the libc resolver will e.g. return an error +to the application that tried to resolve the domain if no other servers are +available in the resolv.conf). This may be of use sometimes. +Note: the simple_only and fqdn_only constants were added by Paul Rombouts. They +are useful for controlling which name servers (if any) will be used by pdnsd +for resolving simple (single-label) host names. fqdn_only used to stand for +"fully qualified domain name only", but this is actually a misnomer. The names +in queries received by pdnsd are always considered to be fully qualified. If +you do not exactly understand what the options simple_only and fqdn_only are +good for, you are probably better off not using them. +The default for this option is included. + +include=string; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is queried if this was the first matching +rule (see also the entry for policy). +If the given name starts with a dot, the whole subdomain of the given name +including the one of that name is matched, e.g. ".foo.bar." will match the +domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +If it does not start in a dot, only exactly the given name (ignoring the case, +of course) will be matched (hint: if you want to include all subdomains, but +not the domain of the given name itself, place an exact-match exclude rule +before the include rule, e.g: exclude="foo.bar."; include=".foo.bar."; +Previous versions of pdnsd required that names given with this and the next +option ended in a dot, but since version 1.1.8b1-par8, pdnsd automatically adds +a dot at the end if it is missing. +pdnsd now also accepts a more compact notation for adding several "include" +entries in one line, e.g.: + +include=".foo",".bar",".my.dom"; + +exclude=string; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is not queried if this was the first +matching rule (see also the entry for policy). +If the given name starts with a dot, the whole subdomain of the given name +including the one of that name is matched, e.g. ".foo.bar." will match the +domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +If it does not start in a dot, only exactly the given name (ignoring the case, +of course) will be matched (hint: if you want to exclude all subdomains, but +not the domain of the given name itself, place an exact-match include rule +before the exclude rule, e.g: include="foo.bar."; exclude=".foo.bar."; +pdnsd now also accepts a more compact notation for adding several "exclude" +entries in one line, e.g.: + +exclude=".foo",".bar",".my.dom"; + + +2.1.3 rr Section + +Every rr section specifies a dns resource record that is stored locally. It +allows you to specify own dns records that are served by pdnsd in a limited +way. Only A, PTR, CNAME, MX, NS and SOA records are implemented. +This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. +and localhost. (and perhaps even one or two hosts) without having to start an +extra named if your cached name servers do not serve those records. It is NOT +intended and not capable to work as a full-featured name server. + + +name=string; +Specifies the name of the resource records, i.e. the domain name of the +resource the record describes. This option must be specified before any a, ptr, +cname, mx, ns or soa records. Names are interpreted as absolute domain names +(i.e. pdnsd assumes they end in the root domain). For this and all following +arguments that take domain names, you need to specify domain names in dotted +notation (example venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. +New in version 1.2: It is also possible to specify a name starting with the +label *. Such a name is called a wildcard. The * in a wildcard can match one or +more labels in a queried name, but only whole labels. Any other * characters in +a wildcard, apart from the leading one, will only match a literal *. +For example, *.mydomain will match a.mydomain or www.a.mydomain, but not +mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. +*a.mydomain will only match itself. +Before you can specify an rr section with name=*.mydomain you must define some +records for mydomain, typically NS and/or SOA records. Example: + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } + +In this example, www.mydomain and ftp.mydomain will resolve to the numeric +address 192.168.1.10 (unless you add rr sections explicitly specifying +different addresses for www.mydomain or ftp.mydomain). If you want mydomain +also to resolve to a numeric address, add an A record to the first rr section. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records for this rr +section. This means that pdnsd flags the domain record so that records of this +domain that are not present in the cache are treated as non-existent, i.e. no +other servers are queried for that record type, and an response containing none +of those records is returned. This is most time what people want: if you add an +A record for a host, and it has no AAAA record (thus no IPv6 address), you +normally don't want other name servers to be queried for it. +This is on by default. +Please note that this only has an effect if it precedes the name option! + +reverse=(on|off); +New in version 1.2: If you want a locally defined name to resolve to a numeric +address and vice versa, you can achieve this by setting reverse=on before +defining the A record (see below). The alternative is to define a separate PTR +record, but you will probably find this option much more convenient. +The default is off. + +a=string; +Defines an A (host address) record. The argument is an IPv4 address in dotted +notation. pdnsd will serve this address for the host name given in the name +option. +Provided there is sufficient support in the C libraries and support for AAAA +records was not disabled, the argument string may also be an IPv6 address, in +which case an AAAA record will be defined. +This option be may used multiple times within an rr section, causing multiple +addresses to be defined for the name. However, if you put the different +addresses in different rr sections for the same name, the definition in the +last rr section will cancel the definitions in the previous ones. + +ptr=string; +Defines a PTR (domain name pointer) record. The argument is a host name in +dotted notation (see name). The ptr record is for resolving adresses into +names. For example, if you want the adress 127.0.0.1 to resolve into localhost, +and localhost into 127.0.0.1, you need something like the following sections: + + rr { + name = localhost; + a = 127.0.0.1; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + +The second section is for reverse resolving and uses the ptr option. Note that +you can get the same effect by specifying only the first rr section with +reverse=on. +There is something special about the name in the second section: when a +resolver wants to get a host name from an internet address, it composes an +address that is built of the IP address in reverse byte order (1.0.0.127 +instead of 127.0.0.1) where each byte of the adress written as number +constitutes a sub-domain under the domain in-addr.arpa. +So, if you want to compose an adress for reverse resolving, take your ip in +dotted notation (e.g. 1.2.3.4), reverse the byte order (4.3.2.1) and append +in-addr.arpa. (4.3.2.1.in-addr.arpa.) Then, define an rr section giving this +address as name and the domain name corresponding to that ip in the ptr option. + +cname=string; +Defines a CNAME (canonical name) record. The argument should be a +fully-qualified host name in dotted notation (see name). A CNAME is the DNS +equivalent of an alias or symbolic link. +A useful application for CNAMEs is giving short, easy to remember nicknames to +hosts with complicated names. For example, you might want the name "news" to +refer to your ISP's news server "nntp2.myisp.com". Instead of adding an A +record for "news" with the same address as "nntp2.myisp.com", you could put in +a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news +server changes, there is no need to update the record for "news". +To implement this with pdnsd, you could add the following section to your +configuration file: + + rr { + name = news; + cname = nntp2.myisp.com; + owner = localhost; + } + +mx=string,number; +Defines an MX (mail exchange) record. The string is the host name of the mail +server in dotted notation (see name). The number specifies the preference +level. +When you send mail to someone, your mail typically goes from your E-mail client +to an SMTP server. The SMTP server then checks for the MX record of the domain +in the E-mail address. For example, with joe@example.com, it would look for the +MX record for example.com and find that the name of mail server for that domain +is, say, mail.example.com. The SMTP server then gets the A record for +mail.example.com, and connects to the mail server. +If there are multiple MX records, the SMTP server will pick one based on the +preference level (starting with the lowest preference number, working its way +up). +Don't define MX records with pdnsd unless you know what you're doing. + +owner=string; +or +ns=string; +Defines an NS (name server) record. Specifies the name of the host which should +be authoritative for the records you defined in the rr section. This is +typically the host pdnsd runs on. +Note: In previous versions of pdnsd this option had to be specified before any +a, ptr, cname, mx or soa entries. In version 1.2, the restrictions on this +option are same as the options just mentioned, and it must listed after the +name= option. This can be a pain if you want to use an old config file which +specifies owner= before name= (sorry about that). Apart from greater +consistency, the advantage is that you can now specify as many NS records as +you like (including zero). + +soa=string,string,number,timespec,timespec,timespec,timespec; +This defines a soa (start of authority) record. The first string is the domain +name of the server and should be equal to the name you specified as owner. +The second string specifies the email address of the maintainer of the name +server. It is also specified as a domain name, so you will have to replace the +@ sign in the name with a dot (.) to get the name you have to specify here. The +next parameter (the first number) is the serial number of the record. You +should increment this number if you change the record. +The 4th parameter is the refresh timeout. It specifies after what amount of +time a caching server should attempt to refresh the cached record. +The 5th parameter specifies a time after which a caching server should attempt +to refresh the record after a refresh failure. +The 6th parameter defines the timeout after which a cached record expires if it +has not been refreshed. +The 7th parameter is the ttl that is specified in every rr and should be the +same as given with the ttl option (if you do not specify a ttl, use the default +86400). + +txt=string,...,string; +New in version 1.2.9: Defines an TXT record. You can specify one or more +strings here. + + +2.1.4 neg Section + +Every neg section specifies a dns resource record or a dns domain that should +be cached negatively locally. Queries for negatively cached records are always +answered immediatley with an error or an empty answer without querying other +hosts as long as the record is valid. The records defined with neg sections +remain valid until they are explicitely invalidated or deleted by the user +using pdnsd-ctl. +This is useful if a certain application asks periodically for nonexisting hosts +or RR types and you do not want a query to go out every time the cached record +has timed out. Example: Netscape Communicator will ask for the servers news and +mail on startup if unconfigured. If you do not have a dns search list for your +network, you can inhibit outgoing queries for these by specifying + + neg { + name = news; + types = domain; + } + neg { + name = mail; + types = domain; + } + +in your config file. If you have a search list, you have to repeat that for any +entry in your search list in addition to the entries given above! +In versions 1.1.11 and later, if you negate whole domains this way, all +subdomains will be negated as well. Thus if you specify +neg {name=example.com; types=domain;} in the config file, this will also negate +www.example.com, xxx.adserver.example.com, etc. + + +name=string; +Specifies the name of the domain for which negative cache entries are created. +This option must be specified before the types option. Names are interpreted as +absolute domain names (i.e. pdnsd assumes they end in the root domain). You +need to specify domain names in dotted notation (example venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +types=(domain|rr_type[,rr_type[,rr_type[,...]]]); +Specifies what is to be cached negatively: domain will cache the whole domain +negatively; alternatively, you can specify a comma-separated list of RR types +which are to be cached negatively. You may specify multiple types options, but +domain and the RR types are mutually exclusive. +The RR types are specified using their official names from the RFC's in +capitals, e.g. A, CNAME, NS, PTR, MX, AAAA, ... +The command pdnsd-ctl list-rrtypes will give you a complete list of those +types. pdnsd-ctl is built along with pdnsd and will be installed in the same +directory as the pdnsd binary during make install. + + +2.1.5 source Section + +Every source section allows you to let pdnsd read the records from a file in an +/etc/hosts-like format. pdnsd will generate records to resolve the entries +address from its host name and vice versa for every entry in the file. This is +normally easier than defining an rr for every of your addresses, since +localhost and your other FQDNs are normally given in /etc/hosts. +The accepted format is as follows: The #-sign initiates a comment, the rest of +the line from the first occurence of this character on is ignored. Empty lines +are tolerated. +The first entry on a line (predeceded by an arbitrary number of tabs and +spaces) is the IP in dotted notation, the second entry on one line (separated +by the first by an arbitrary number of tabs and spaces) is the FQDN (fully +qualified domain name) for that ip. The rest of the line is ignored by default +(in the original /etc/hosts, it may contain information not needed by pdnsd). + + +owner=string; +Specifies the name of the host pdnsd runs on and that are specified in dns +answers (specifically, nameserver records). Must be specified before any file +entries. +Names are interpreted as absolute domain names (i.e. pdnsd assumes they end in +the root domain). You need to specify domain names in dotted notation (example +venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +file=string; +The string specifies a file name. For every file entry in a source section, +pdnsd will try to load the given file as described above. Failure is indicated +only when the file cannot be opened, malformed entries will be ignored. + +serve_aliases=(on|off); +If this is turned on pdnsd will serve the aliases given in a hosts-style file. +These are the third entry in a line of a hosts-style file, which usually give a +"short name" for the host. This may be used to support broken clients without a +proper domain-search option. If no aliases are given in a line of the file, +pdnsd behaves as without this option for this line. +This feature was suggested by Bert Frederiks. +It is off by default. + +authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records with the +data from the hosts file. Please see the description of the option of the same +name in the rr section for a closer description of what this means. Please note +that this only has an effect for files sourced with file options subsequent to +this option. +This is on by default. + + +2.1.6 include Section + +A configuration file may include other configuration files. However, only the +top-level configuration file may contain global and server sections, thus +include files are effectively limited to sections that add local definitions to +the cache. +Include sections currently only have one type of option, which may be given +multiple times within a single section. + + +file=string; +The string specifies a file name. For every file option in an include section, +pdnsd will parse the given file as described above. The file may contain +include sections itself, but as a precaution pdnsd checks that a certain +maximum depth is not exceeded to guard against the possibility of infinite +recursion. + + +3 pdnsd-ctl + +pdnsd-ctl allows you to configure pdnsd at run time. To make this work, you +have to start pdnsd with the -s option (or use the status_ctl option in the +config file). You also should make sure that you have appropriate permissions +on the control socket (use the ctl_perms option to make this sure) and of your +pdnsd cache directory (pdnsd keeps its socket there). Please make sure the +pdnsd cache directory is not writeable for untrusted users! + +pdnsd-ctl accepts two command-line options starting with a dash. +-c may be used to specify the cache directory (and takes this as argument). The +default for this setting is the pdnsd default cache directory (specified at +compile time). The cache directory for pdnsd-ctl must be the same pdnsd uses! +-q can be used to make the output of pdnsd-ctl less verbose. + +The following table lists the commands that pdnsd-ctl supports. The command +must always be the first command-line option (not starting with a dash), the +arguments to the command must follow in the given order. +In the following table, keywords are in a normal font, while placeholders are +in italics. +Alternatives are specified like (alt1|alt2|alt3). Optional arguments are placed +between square brackets []. + +Command Arguments Description + +help Print a command summary. + +version Print version and license info. + +status Print a description of pdnsd's cache status, thread status + and configuration. Also shows which remote name servers + are assumed to be available. + +server (index|label) (up| Set the status of the server with the given index or label + down|retest) [dns1[, (where the given label matches the one given with the + dns2[,...]]] label option in the respective server section in the + config file) to up or down, or force a retest. The index + is assigned in the order of definition in pdnsd.conf + starting with 0. Use the status command to view the + indices and labels. You can specify all instead of an + index or label to perform the action for all servers + registered with pdnsd. Example: + pdnsd-ctl server 0 retest + An optional third argument consisting of a list of IP + addresses (separated by commas or white-space characters) + can be given. This list will replace the previous list of + addresses of name servers used by pdnsd in the specified + section of the config file. For example in the /etc/ppp/ + ip-up script called by pppd you could place the following + line: + pdnsd-ctl server isplabel up $DNS1,$DNS2 + If white space is used to separate addresses the list will + have to be quoted. Spurious commas and white-space + characters are ignored. The last argument may also be an + empty string, in which case the existing IP addresses are + removed and the corresponding server section becomes + inactive. + +record name (delete| Delete or invalidate the records of the given domain name + invalidate) if it is in the cache. Invalidation means that the records + are marked as timed out, and will be reloaded if possible + (if purge_cache is set to on, they will be deleted in any + case). + For local records (i.e., records that were given in the + config file using a rr section, records read from a + hosts-style file and records added using pdnsd-ctl), + invalidation has no effect. Deletion will work, though. + Example: + pdnsd-ctl record localhost. delete + +source fn owner [ttl] [(on Load a hosts-style file. Works like using the pdnsd source + |off)] [noauth] configuration section. owner and ttl are used as in the + source section. ttl has a default of 900 (it does not need + to be specified). The next to last argument corresponds to + the serve_aliases option, and is off by default (i.e. if + it is not specified). noauth is used to make the domains + non-authoritative - please see the description of the + authrec config file options for a description of what that + means. fn is the filename. The file must be readable by + pdnsd! Example: + pdnsd-ctl source /etc/hosts localhost. 900 off + +add a addr name [ttl] + [noauth] + Add a record of the given type to the pdnsd cache, + replacing existing records for the same name and type. The +add aaaa addr name [ttl 2nd argument corresponds to the value of the option in the + ] [noauth] rr section that is named like the first argument: a is a + record for hostname-to-address mapping, aaaa is the same + thing for IPv6 addresses, and ptr is for +add ptr host name [ttl] address-to-hostname mapping. See the documentation for the + [noauth] rr section for more details. In case of A and AAAA + records, the addr argument may be a list of IP addresses, + separated by commas or white space, causing multiple +add cname host name [ addresses to be defined for the same name. The ttl is + ttl] [noauth] optional, the default is 900 seconds. noauth is used to + make the domains non-authoritative - please see the + description of the authrec config file options for a +add mx host name pref description of what that means. If you want no other + [ttl] [noauth] record than the newly added in the cache, do + pdnsd-ctl record name delete before adding records. This + is also better when overwriting local records. Example: +add ns host name [ttl] pdnsd-ctl add a 127.0.0.1 localhost. 900 + [noauth] + + +neg name [type] [ttl] Add a negatively cached record to pdnsd's cache, replacing + existing records for the same name and type. If no type is + given, the whole domain is cached negatively. For + negatively cached records, errors are immediately returned + on a query, without querying other servers first. The ttl + is optional, the default is 900 seconds. + You can get a list of all types you can pass to this + command using pdnsd-ctl list-rrtypes. The type is treated + case-sensitive! Example: + pdnsd-ctl neg foo.bar A 900 + pdnsd-ctl neg foo.baz 900 + +config [filename] Reload pdnsd's configuration file. + The config file must be owned by the uid that pdnsd had + when it was started, and be readable by pdnsd's run_as + uid. If no file name is specified, the config file used at + start-up is reloaded. + Note that some configuration changes, like the port or IP + address pdnsd listens on, cannot be made this way and you + will receive an error message. In these cases, you will + have to restart pdnsd instead. + +include filename Parse the given file as an include file, see the + documentation on include sections for a description what + this file may contain. + This command is useful for adding definitions to the cache + without reconfiguring pdnsd. + +eval string Parse the given string as if it were part of pdnsd's + configuration file. The string should hold one or more + complete configuration sections. However, global and + server sections are not allowed, just as in include files. + If multiple strings are given, they will be joined using + newline chars and parsed together. + This command is useful for adding records interactively to + the cache that cannot be defined using the "pdnsd-ctl add" + command, (e.g. soa records). + +empty-cache [[+|-]name ...] If no arguments are provided, the cache will be completely + emptied, freeing all existing entries. Note that this also + removes local records, as defined by the config file. To + restore local records, run "pdnsd-ctl config" or + "pdnsd-ctl include filename" immediately afterwards. + The "pdnsd-ctl empty-cache" command now accepts additional + arguments; these are interpreted as include/exclude names. + If an argument starts with a '+' the name will be + included. If an argument starts with a '-' it will be + excluded. If an argument does not begin with '+' or '-', a + '+' is assumed. If the domain name of a cache entry ends + in one of the names in the list, the first match will + determine what happens. If the matching name is to be + included, the cache entry is deleted, otherwise not. If + there are no matches, the default action is not to delete. + Note that if you want to delete exactly one name and no + others, you should use "pdnsd-ctl record name delete", + this is also much more efficient. + Examples: + pdnsd-ctl empty-cache + This command will remove all cache entries. + + pdnsd-ctl empty-cache microsoft.com msft.net + This will remove all entries ending in microsoft.com or + msft.net. + + pdnsd-ctl empty-cache -localdomain -168.192.in-addr.arpa . + This will remove all entries except those ending in + localdomain or 168.192.in-addr.arpa. Note that '.' is the + root domain which matches any domain name. + +dump [name] Print information stored in the cache about name. If name + begins with a dot and is not the root domain, information + about the names in the cache ending in name (including + name without the leading dot) will be printed. If name is + not specified, information about all the names in the + cache will be printed. + For each RR record the time and date that this record has + been added to the cache will be printed in the form mm/dd + HH:MM:SS (locally defined records are printed without a + time stamp). After that the type of record is printed with + the data. For the more common types of RR records the data + will be printed in human readable form, the remaining ones + in a hexadecimal representation. + This command is mainly useful for diagnostic purposes. + Note that if you pipe the output of this command through + an application that reads only part of the output and then + blocks (such as more or less), pdnsd will not be able to + add new entries to the cache until the pipe is closed. It + is preferable to capture the output in a file in such a + case. + +list-rrtypes List available rr types for the neg command. Note that + those are only used for the neg command, not for add! + + + +4 contrib/ + +The contrib directory in the pdnsd distribution contains useful +user-contributed scripts. +So far, there are scripts contributed by Marko Stolle and Paul Rombouts that +make pdnsd usable in a DHCP setup. Please take a look into the README file in +the contrib directory for further information. + + +5 Problems... + +If you have problems with configuring or running pdnsd, be sure to read the FAQ +. If this does not help you, pdnsd crashes or you find bugs, please mail one of +the authors. +Note added by Paul A. Rombouts: Thomas Moestl no longer maintains the code. I +have revised the code and added new features. See README.par and the ChangeLog +in the source directory (or /usr/share/doc/pdnsd- if you have +installed a binary package) for more details. If you have questions about my +modifications, you can find my email address at the end of README.par. + + +6 Hacking + +Here comes some information you might find useful for hacking pdnsd. + +6.1 Source files + +Makefile.am, autoconf/automake/autoheader scripts. Makefile.am's are in +configure.in, most subdirectories. +acconfig.h + +pdnsd.spec.in A template from which configure generates a spec file for + building rpm's for various distributions. + +version Contains only the program version string. Needed for + several templates. + +src/rc/* rc (start-up) scripts for various linux distributions. + + The pdnsd cache subsystem(s) as defined in src/cache.h. +src/cache.c This is the "traditional" pdnsd system which keeps the + cache in memory and uses hash tables for accesses. Sourav + K. Mandal is working on a system using gdbm. + +src/pdnsd-ctl/* Contains the code for pdnsd-ctl, a program that allows you + to control pdnsd at run time. + + The lex/flex source file for the config file lexer. This is +src/conf-lex.l.in a template because there might be inserted "%option + yylineno" for proper flex support. (obsolete, superseded by + src/conf-parser.c) + + This is automatically generated by configure from +src/conf-lex.l conf-lex.l.in. It may be overwritten in any make, so never + modify this, but conf-lex.l.in instead! (obsolete, + superseded by src/conf-parser.c) + +src/conf-parse.y The yacc/bison source of the config file parser. (obsolete, + superseded by src/conf-parser.c) + +src/conf-parser.c, The config file parser written purely in C (versions +src/conf-parser.h, 1.1.10-par and later). +src/conf-keywords.h + +src/conff.c, src/ The configuration handler functions and their prototypes. +conff.h The parser is called from here. + +src/consts.h Some constants used by the parser, config file handler + functions and in the server status thread, among others. + + Define dns message structures, constants, and some common +src/dns.c, src/dns.h dns data handlers. dns.h contains gcc-specific code (in + praticular, "__attribute__((packed))"). + +src/dns_answer.c, Define functions that answer incoming dns queries. +src/dns_answer.h + +src/dns_query.c, src Define functions to manage outgoing dns queries. +/dns_query.h + +src/error.c, src/ Functions for error output to stderr or the syslog, and +error.h debug output to stderr or pdnsd.debug. + +src/hash.c, src/ Contains the code for storing and looking up cache entries +hash.h in the hash table. + +src/helpers.c, src/ Define miscellaneous helper functions. +helpers.h + +src/icmp.c, src/ Define a function for performing a ping test. This contains +icmp.h OS-specific code. + +src/main.c Contains main(), which holds the command line parser, + performs initialisations and signal handling. + + Contains the code for the executable make_hashconvtable, +src/ which is only run once, during build time, to generate the +make_hashconvtable.c file hashconvtable.h, used by src/hash.c (versions + 1.1.10-par and later). (obsolete since version 1.2) + + A perl script for generating src/rr_types.h, a C header +src/ file containing macro definitions and tables needed for +make_rr_types_h.pl handling the RR types known to pdnsd, from the text file + src/rr_types.in. + +src/rr_types.c, src/ These define tables and macros needed for handling the RR +rr_types.h, src/ types known to pdnsd. Since version 1.2.9, rr_types.h is an +rr_types.in automatically generated file, see make_rr_types_h.pl. + +src/netdev.c, src/ Define functions for network device handling. OS-specific. +netdev.h + +src/servers.c, src/ Define functions for the server status thread that performs +servers.h the periodical uptests. + +src/status.c, src/ Define functions for the status control thread. This is +status.h pdnsd's interface to pdnsd-ctl. + + +------------------------------------------------------------------------------- + +Copyright (C) 2000, 2001 Thomas Moestl +Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2012 Paul A. Rombouts + +Last revised: 19 April 2012 by Paul A. Rombouts + diff --git a/app/src/main/jni/pdnsd/file-list.base.in b/app/src/main/jni/pdnsd/file-list.base.in new file mode 100644 index 0000000..409ba33 --- /dev/null +++ b/app/src/main/jni/pdnsd/file-list.base.in @@ -0,0 +1,5 @@ +%defattr(-,root,root) +%doc AUTHORS THANKS COPYING COPYING.BSD ChangeLog ChangeLog.old INSTALL NEWS README README.par README.par.old TODO +%config /etc/pdnsd.conf.sample +%attr(750, @def_id@, @def_id@) %dir @cachedir@ +%attr(640, @def_id@, @def_id@) %config @cachedir@/pdnsd.cache diff --git a/app/src/main/jni/pdnsd/install-sh b/app/src/main/jni/pdnsd/install-sh new file mode 100644 index 0000000..4d4a951 --- /dev/null +++ b/app/src/main/jni/pdnsd/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/app/src/main/jni/pdnsd/missing b/app/src/main/jni/pdnsd/missing new file mode 100644 index 0000000..894e786 --- /dev/null +++ b/app/src/main/jni/pdnsd/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-06-08.21 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/app/src/main/jni/pdnsd/pdnsd.spec.in b/app/src/main/jni/pdnsd/pdnsd.spec.in new file mode 100644 index 0000000..6af6cde --- /dev/null +++ b/app/src/main/jni/pdnsd/pdnsd.spec.in @@ -0,0 +1,244 @@ +# rpmbuild spec file for pdnsd. +# with modifications by Paul Rombouts. + +# Supported rpmbuild --define and --with options include: +# +# --with isdn Configure with --enable-isdn. +# +# --without poll Configure with --disable-poll +# +# --without nptl Configure with --with-thread-lib=linuxthreads. +# +# --with ipv6 Configure with --enable-ipv6. +# +# --without tcpqueries Configure with --disable-tcp-queries. +# +# --without debug Configure with --with-debug=0. +# +# --define "distro " Configure with --with-distribution=. +# +# --define "run_as_user " Configure with --with-default-id=. +# For RPMs the default is "@def_id@". +# +# --define "run_as_uid " If the user defined by the previous option does not exist +# when the RPM is installed, the pre-install script will try +# to create a new user with numerical id . +# +# --define "cachedir " Configure with --with-cachedir=. +# + +%{!?distro: %define distro @distribution@} + +# The default run_as ID to use +%{!?run_as_user: %define run_as_user @def_id@} +# By default, if a new run_as_user is to be created, we let +# useradd choose the numerical uid, unless run_as_uid is defined. +#define run_as_uid 96 +%{!?cachedir: %define cachedir @cachedir@} +%define conffile %{_sysconfdir}/pdnsd.conf + +Summary: A caching dns proxy for small networks or dialin accounts +Name: @PACKAGE@ +Version: @VERSION@ +Release: @packagerelease@ +License: GPLv3 +Group: Daemons +Source: http://members.home.nl/p.a.rombouts/pdnsd/releases/%{name}-%{version}-%{release}.tar.gz +URL: http://members.home.nl/p.a.rombouts/pdnsd.html +Vendor: Paul A. Rombouts +Packager: Paul A. Rombouts +Prefix: %{_prefix} +BuildRoot: %{_tmppath}/%{name}-%{version}-root + +%description +pdnsd is a proxy DNS daemon with permanent (disk-)cache and the ability +to serve local records. It is designed to detect network outages or hangups +and to prevent DNS-dependent applications like Netscape Navigator from hanging. + +The original author of pdnsd is Thomas Moestl, but pdnsd is no longer maintained +by him. This is an extensively revised version by Paul A. Rombouts. +For a description of the changes see http://members.home.nl/p.a.rombouts/pdnsd.html +and the file README.par in %{_docdir}/%{name}-%{version} + +%{!?distro:You can specify the target distribution when you build the source RPM. For instance, if you're building for a Red Hat system call rpmbuild with:} +%{!?distro: --define "distro RedHat"} +%{?distro:This package was built for a %{distro} distribution.} +%{!?_with_isdn:It's possible to rebuild the source RPM with isdn support using the rpmbuild option:} +%{!?_with_isdn: --with isdn} +%{?_with_isdn:This package was built with isdn support enabled.} +%{!?_with_ipv6:It's possible to rebuild the source RPM with ipv6 support using the rpmbuild option:} +%{!?_with_ipv6: --with ipv6} +%{?_with_ipv6:This package was built with ipv6 support.} +%{?_without_poll:This package was built with the select(2) function instead of poll(2).} + +%prep +%setup + +%build +CFLAGS="${CFLAGS:-$RPM_OPT_FLAGS -Wall}" ./configure \ + --prefix=%{_prefix} --sysconfdir=%{_sysconfdir} --mandir=%{_mandir} \ + --with-cachedir="%{cachedir}" \ + %{?distro:--with-distribution=%{distro}} --enable-specbuild \ + --with-default-id=%{run_as_user} \ + %{?_with_isdn:--enable-isdn} \ + %{?_without_poll:--disable-poll} \ + %{?_without_nptl:--with-thread-lib=linuxthreads} \ + %{?_with_ipv6:--enable-ipv6} \ + %{?_without_tcpqueries:--disable-tcp-queries} \ + %{?_without_debug:--with-debug=0} + +make + +%install +%if "%{run_as_user}" != "nobody" +[ "$(id -un)" != root ] || +id -u %{run_as_user} > /dev/null 2>&1 || +/usr/sbin/useradd -c "Proxy DNS daemon" %{?run_as_uid:-u %{run_as_uid}} \ + -s /sbin/nologin -r -d "%{cachedir}" %{run_as_user} || { + set +x + echo "Cannot create user \"%{run_as_user}\"%{?run_as_uid: with uid=%{run_as_uid}}" + echo "Please select another numerical uid and rebuild with --define \"run_as_uid uid\"" + echo "or create a user named \"%{run_as_user}\" by hand and try again." + exit 1 +} +%endif + +rm -rf "$RPM_BUILD_ROOT" +make DESTDIR="$RPM_BUILD_ROOT" install +cp -f file-list.base file-list +find doc contrib -not -type d -not -iname '*makefile' -not -name '*.am' \ + -not -name '*.in' -not -path 'doc/*.pl' | +sed -e 's/^/%doc --parents /' >> file-list +CURDIR=$PWD; cd "$RPM_BUILD_ROOT" +find . -not -type d '(' -not -name 'pdnsd.conf*' -or -name 'pdnsd.conf.[1-9]*' ')' \ + -not -path '.%{_docdir}/*' -not -path './var/*' | +sed -e 's/^\.// + \:/man:{ + /\.gz$/!s/$/.gz/ + }' >> "$CURDIR/file-list" + +%clean +rm -rf "$RPM_BUILD_ROOT" +#rm -rf %{_builddir}/%{name}-%{srcver} + +%files -f file-list + +%pre +# First stop any running pdnsd daemons +%if "%{distro}" == "SuSE" +/sbin/init.d/pdnsd stop >/dev/null 2>&1 +%endif +%if "%{distro}" == "RedHat" +if [ -f /var/lock/subsys/pdnsd ]; then + if /sbin/pidof pdnsd > /dev/null; then + /sbin/service pdnsd stop >/dev/null 2>&1 + if [ "$1" -ge 2 ]; then touch /var/lock/subsys/pdnsd; fi + else + rm -f /var/lock/subsys/pdnsd + fi +fi +%endif + +%if "%{run_as_user}" != "nobody" +# Add the "pdnsd" user +id -u %{run_as_user} > /dev/null 2>&1 || +/usr/sbin/useradd -c "Proxy DNS daemon" %{?run_as_uid:-u %{run_as_uid}} \ + -s /sbin/nologin -r -d "%{cachedir}" %{run_as_user} || { + echo "Cannot create user \"%{run_as_user}\"%{?run_as_uid: with uid=%{run_as_uid}}" + echo "Please create a user named \"%{run_as_user}\" by hand and try again." + exit 1 +} +[ "$(id -gn %{run_as_user})" = %{run_as_user} ] || { + echo "user \"%{run_as_user}\" does not have an corresponding group called \"%{run_as_user}\"" + echo "Please change the initial group of user \"%{run_as_user}\" to \"%{run_as_user}\" and try again." + exit 1 +} + +if [ -f "%{conffile}" ] && + grep -v -e '^[[:blank:]]*\(#\|\/\/\)' "%{conffile}" | + grep -q -e '\ "%{conffile}" && + echo "In %{conffile} runs_as=\"nobody\" has been replaced by run_as=\"%{run_as_user}\"" +fi +%endif + +if [ -f "%{cachedir}/pdnsd.cache" ]; then + chown -c %{run_as_user}:%{run_as_user} "%{cachedir}/pdnsd.cache" +fi + +%post +%if "%{distro}" == "SuSE" +if [ -w /etc/rc.config ]; then + grep "START_PDNSD" /etc/rc.config > /dev/null + if [ $? -ne 0 ] ; then + echo -e \ +"\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" \ +>> /etc/rc.config + fi +fi +%endif +%if "%{distro}" == "RedHat" +if [ "$1" = 1 ]; then + /sbin/chkconfig --add pdnsd +fi +%endif + +%preun +%if "%{distro}" == "RedHat" +if [ "$1" = 0 ]; then + /sbin/service pdnsd stop >/dev/null 2>&1 + /sbin/chkconfig --del pdnsd +fi +%endif + +%postun +%if "%{distro}" == "RedHat" +if [ "$1" -ge 1 ]; then + /sbin/service pdnsd condrestart >/dev/null 2>&1 +fi +%endif + +%changelog +* Tue Jan 31 2012 Paul A. Rombouts +- Prevent makefiles and perl scripts from being installed + in the documentation directory. +* Sat Jan 28 2012 Paul A. Rombouts +- Update the (Source) URLs. +* Sat Aug 4 2007 Paul Rombouts +- License is now GPL version 3 +* Fri Mar 24 2006 Paul Rombouts +- Instead of using a fixed default value for run_as_uid, + I let useradd choose the uid if run_as_uid is undefined. +* Thu Dec 29 2005 Paul Rombouts +- TCP-query support is now compiled in by default, + but can be disabled using "--without tcpqueries". +* Sun Jul 20 2003 Paul Rombouts +- Changed default run_as ID from "nobody" to "pdnsd" +* Fri Jun 20 2003 Paul Rombouts +- Added configuration option for NPTL. +* Sat Jun 07 2003 Paul Rombouts +- Added automatic definition of distro using _vendor macro. +* Thu May 22 2003 Paul Rombouts +- Ensured that modification times of acconfig.h and configure.in + are not changed by patching to avoid unwanted reconfigure during make phase. +* Tue May 20 2003 Paul Rombouts +- Applied my customized patch file. See READ.par for details. +* Sun May 16 2001 Thomas Moestl +- Make use of chkconfig for Red Hat (patch by Christian Engstler) +* Sun Mar 25 2001 Thomas Moestl +- Merged SuSE fixes by Christian Engstler +* Fri Feb 09 2001 Thomas Moestl +- Merged in a spec fix for mapage inclusion contributed by Sourav K. + Mandal +* Sun Nov 26 2000 Thomas Moestl +- Added some patches contributed by Bernd Leibing +* Tue Aug 15 2000 Thomas Moestl +- Added the distro for configure +* Tue Jul 11 2000 Sourav K. Mandal +- autoconf/automake modifications diff --git a/app/src/main/jni/pdnsd/src/Makefile.am b/app/src/main/jni/pdnsd/src/Makefile.am new file mode 100644 index 0000000..052022d --- /dev/null +++ b/app/src/main/jni/pdnsd/src/Makefile.am @@ -0,0 +1,24 @@ + +sbin_PROGRAMS = pdnsd + +pdnsd_CFLAGS = $(thread_CFLAGS) + +pdnsd_SOURCES = conf-parser.c conff.c consts.c debug.c dns.c dns_answer.c \ + dns_query.c error.c helpers.c icmp.c list.c main.c netdev.c rr_types.c \ + status.c servers.c thread.c cache.c hash.c conf-parser.h \ + conf-keywords.h conff.h consts.h debug.h dns.h dns_answer.h \ + dns_query.h error.h helpers.h icmp.h ipvers.h list.h netdev.h \ + rr_types.h servers.h status.h thread.h cache.h hash.h pdnsd_assert.h \ + freebsd_netinet_ip_icmp.h + +EXTRA_DIST = make_rr_types_h.pl rr_types.in + +## Try to do this last + +SUBDIRS = . pdnsd-ctl rc test + +$(pdnsd_OBJECTS): rr_types.h + +rr_types.h: make_rr_types_h.pl rr_types.in + perl make_rr_types_h.pl rr_types.in > rr_types.h + diff --git a/app/src/main/jni/pdnsd/src/Makefile.in b/app/src/main/jni/pdnsd/src/Makefile.in new file mode 100644 index 0000000..1cc64f3 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/Makefile.in @@ -0,0 +1,921 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +sbin_PROGRAMS = pdnsd$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am_pdnsd_OBJECTS = pdnsd-conf-parser.$(OBJEXT) pdnsd-conff.$(OBJEXT) \ + pdnsd-consts.$(OBJEXT) pdnsd-debug.$(OBJEXT) \ + pdnsd-dns.$(OBJEXT) pdnsd-dns_answer.$(OBJEXT) \ + pdnsd-dns_query.$(OBJEXT) pdnsd-error.$(OBJEXT) \ + pdnsd-helpers.$(OBJEXT) pdnsd-icmp.$(OBJEXT) \ + pdnsd-list.$(OBJEXT) pdnsd-main.$(OBJEXT) \ + pdnsd-netdev.$(OBJEXT) pdnsd-rr_types.$(OBJEXT) \ + pdnsd-status.$(OBJEXT) pdnsd-servers.$(OBJEXT) \ + pdnsd-thread.$(OBJEXT) pdnsd-cache.$(OBJEXT) \ + pdnsd-hash.$(OBJEXT) +pdnsd_OBJECTS = $(am_pdnsd_OBJECTS) +pdnsd_LDADD = $(LDADD) +pdnsd_LINK = $(CCLD) $(pdnsd_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(pdnsd_SOURCES) +DIST_SOURCES = $(pdnsd_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +pdnsd_CFLAGS = $(thread_CFLAGS) +pdnsd_SOURCES = conf-parser.c conff.c consts.c debug.c dns.c dns_answer.c \ + dns_query.c error.c helpers.c icmp.c list.c main.c netdev.c rr_types.c \ + status.c servers.c thread.c cache.c hash.c conf-parser.h \ + conf-keywords.h conff.h consts.h debug.h dns.h dns_answer.h \ + dns_query.h error.h helpers.h icmp.h ipvers.h list.h netdev.h \ + rr_types.h servers.h status.h thread.h cache.h hash.h pdnsd_assert.h \ + freebsd_netinet_ip_icmp.h + +EXTRA_DIST = make_rr_types_h.pl rr_types.in +SUBDIRS = . pdnsd-ctl rc test +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +pdnsd$(EXEEXT): $(pdnsd_OBJECTS) $(pdnsd_DEPENDENCIES) + @rm -f pdnsd$(EXEEXT) + $(pdnsd_LINK) $(pdnsd_OBJECTS) $(pdnsd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-conf-parser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-conff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-consts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns_answer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns_query.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-error.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-hash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-helpers.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-icmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-netdev.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-rr_types.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-servers.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-status.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-thread.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +pdnsd-conf-parser.o: conf-parser.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conf-parser.o -MD -MP -MF $(DEPDIR)/pdnsd-conf-parser.Tpo -c -o pdnsd-conf-parser.o `test -f 'conf-parser.c' || echo '$(srcdir)/'`conf-parser.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conf-parser.Tpo $(DEPDIR)/pdnsd-conf-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conf-parser.c' object='pdnsd-conf-parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conf-parser.o `test -f 'conf-parser.c' || echo '$(srcdir)/'`conf-parser.c + +pdnsd-conf-parser.obj: conf-parser.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conf-parser.obj -MD -MP -MF $(DEPDIR)/pdnsd-conf-parser.Tpo -c -o pdnsd-conf-parser.obj `if test -f 'conf-parser.c'; then $(CYGPATH_W) 'conf-parser.c'; else $(CYGPATH_W) '$(srcdir)/conf-parser.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conf-parser.Tpo $(DEPDIR)/pdnsd-conf-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conf-parser.c' object='pdnsd-conf-parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conf-parser.obj `if test -f 'conf-parser.c'; then $(CYGPATH_W) 'conf-parser.c'; else $(CYGPATH_W) '$(srcdir)/conf-parser.c'; fi` + +pdnsd-conff.o: conff.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conff.o -MD -MP -MF $(DEPDIR)/pdnsd-conff.Tpo -c -o pdnsd-conff.o `test -f 'conff.c' || echo '$(srcdir)/'`conff.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conff.Tpo $(DEPDIR)/pdnsd-conff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conff.c' object='pdnsd-conff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conff.o `test -f 'conff.c' || echo '$(srcdir)/'`conff.c + +pdnsd-conff.obj: conff.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conff.obj -MD -MP -MF $(DEPDIR)/pdnsd-conff.Tpo -c -o pdnsd-conff.obj `if test -f 'conff.c'; then $(CYGPATH_W) 'conff.c'; else $(CYGPATH_W) '$(srcdir)/conff.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conff.Tpo $(DEPDIR)/pdnsd-conff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conff.c' object='pdnsd-conff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conff.obj `if test -f 'conff.c'; then $(CYGPATH_W) 'conff.c'; else $(CYGPATH_W) '$(srcdir)/conff.c'; fi` + +pdnsd-consts.o: consts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-consts.o -MD -MP -MF $(DEPDIR)/pdnsd-consts.Tpo -c -o pdnsd-consts.o `test -f 'consts.c' || echo '$(srcdir)/'`consts.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-consts.Tpo $(DEPDIR)/pdnsd-consts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='consts.c' object='pdnsd-consts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-consts.o `test -f 'consts.c' || echo '$(srcdir)/'`consts.c + +pdnsd-consts.obj: consts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-consts.obj -MD -MP -MF $(DEPDIR)/pdnsd-consts.Tpo -c -o pdnsd-consts.obj `if test -f 'consts.c'; then $(CYGPATH_W) 'consts.c'; else $(CYGPATH_W) '$(srcdir)/consts.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-consts.Tpo $(DEPDIR)/pdnsd-consts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='consts.c' object='pdnsd-consts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-consts.obj `if test -f 'consts.c'; then $(CYGPATH_W) 'consts.c'; else $(CYGPATH_W) '$(srcdir)/consts.c'; fi` + +pdnsd-debug.o: debug.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-debug.o -MD -MP -MF $(DEPDIR)/pdnsd-debug.Tpo -c -o pdnsd-debug.o `test -f 'debug.c' || echo '$(srcdir)/'`debug.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-debug.Tpo $(DEPDIR)/pdnsd-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='debug.c' object='pdnsd-debug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-debug.o `test -f 'debug.c' || echo '$(srcdir)/'`debug.c + +pdnsd-debug.obj: debug.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-debug.obj -MD -MP -MF $(DEPDIR)/pdnsd-debug.Tpo -c -o pdnsd-debug.obj `if test -f 'debug.c'; then $(CYGPATH_W) 'debug.c'; else $(CYGPATH_W) '$(srcdir)/debug.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-debug.Tpo $(DEPDIR)/pdnsd-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='debug.c' object='pdnsd-debug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-debug.obj `if test -f 'debug.c'; then $(CYGPATH_W) 'debug.c'; else $(CYGPATH_W) '$(srcdir)/debug.c'; fi` + +pdnsd-dns.o: dns.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns.o -MD -MP -MF $(DEPDIR)/pdnsd-dns.Tpo -c -o pdnsd-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns.Tpo $(DEPDIR)/pdnsd-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns.c' object='pdnsd-dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c + +pdnsd-dns.obj: dns.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns.Tpo -c -o pdnsd-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns.Tpo $(DEPDIR)/pdnsd-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns.c' object='pdnsd-dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` + +pdnsd-dns_answer.o: dns_answer.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_answer.o -MD -MP -MF $(DEPDIR)/pdnsd-dns_answer.Tpo -c -o pdnsd-dns_answer.o `test -f 'dns_answer.c' || echo '$(srcdir)/'`dns_answer.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_answer.Tpo $(DEPDIR)/pdnsd-dns_answer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_answer.c' object='pdnsd-dns_answer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_answer.o `test -f 'dns_answer.c' || echo '$(srcdir)/'`dns_answer.c + +pdnsd-dns_answer.obj: dns_answer.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_answer.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns_answer.Tpo -c -o pdnsd-dns_answer.obj `if test -f 'dns_answer.c'; then $(CYGPATH_W) 'dns_answer.c'; else $(CYGPATH_W) '$(srcdir)/dns_answer.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_answer.Tpo $(DEPDIR)/pdnsd-dns_answer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_answer.c' object='pdnsd-dns_answer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_answer.obj `if test -f 'dns_answer.c'; then $(CYGPATH_W) 'dns_answer.c'; else $(CYGPATH_W) '$(srcdir)/dns_answer.c'; fi` + +pdnsd-dns_query.o: dns_query.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_query.o -MD -MP -MF $(DEPDIR)/pdnsd-dns_query.Tpo -c -o pdnsd-dns_query.o `test -f 'dns_query.c' || echo '$(srcdir)/'`dns_query.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_query.Tpo $(DEPDIR)/pdnsd-dns_query.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_query.c' object='pdnsd-dns_query.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_query.o `test -f 'dns_query.c' || echo '$(srcdir)/'`dns_query.c + +pdnsd-dns_query.obj: dns_query.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_query.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns_query.Tpo -c -o pdnsd-dns_query.obj `if test -f 'dns_query.c'; then $(CYGPATH_W) 'dns_query.c'; else $(CYGPATH_W) '$(srcdir)/dns_query.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_query.Tpo $(DEPDIR)/pdnsd-dns_query.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_query.c' object='pdnsd-dns_query.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_query.obj `if test -f 'dns_query.c'; then $(CYGPATH_W) 'dns_query.c'; else $(CYGPATH_W) '$(srcdir)/dns_query.c'; fi` + +pdnsd-error.o: error.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-error.o -MD -MP -MF $(DEPDIR)/pdnsd-error.Tpo -c -o pdnsd-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-error.Tpo $(DEPDIR)/pdnsd-error.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='pdnsd-error.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c + +pdnsd-error.obj: error.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-error.obj -MD -MP -MF $(DEPDIR)/pdnsd-error.Tpo -c -o pdnsd-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-error.Tpo $(DEPDIR)/pdnsd-error.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='pdnsd-error.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` + +pdnsd-helpers.o: helpers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-helpers.o -MD -MP -MF $(DEPDIR)/pdnsd-helpers.Tpo -c -o pdnsd-helpers.o `test -f 'helpers.c' || echo '$(srcdir)/'`helpers.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-helpers.Tpo $(DEPDIR)/pdnsd-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='helpers.c' object='pdnsd-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-helpers.o `test -f 'helpers.c' || echo '$(srcdir)/'`helpers.c + +pdnsd-helpers.obj: helpers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-helpers.obj -MD -MP -MF $(DEPDIR)/pdnsd-helpers.Tpo -c -o pdnsd-helpers.obj `if test -f 'helpers.c'; then $(CYGPATH_W) 'helpers.c'; else $(CYGPATH_W) '$(srcdir)/helpers.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-helpers.Tpo $(DEPDIR)/pdnsd-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='helpers.c' object='pdnsd-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-helpers.obj `if test -f 'helpers.c'; then $(CYGPATH_W) 'helpers.c'; else $(CYGPATH_W) '$(srcdir)/helpers.c'; fi` + +pdnsd-icmp.o: icmp.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-icmp.o -MD -MP -MF $(DEPDIR)/pdnsd-icmp.Tpo -c -o pdnsd-icmp.o `test -f 'icmp.c' || echo '$(srcdir)/'`icmp.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-icmp.Tpo $(DEPDIR)/pdnsd-icmp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='icmp.c' object='pdnsd-icmp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-icmp.o `test -f 'icmp.c' || echo '$(srcdir)/'`icmp.c + +pdnsd-icmp.obj: icmp.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-icmp.obj -MD -MP -MF $(DEPDIR)/pdnsd-icmp.Tpo -c -o pdnsd-icmp.obj `if test -f 'icmp.c'; then $(CYGPATH_W) 'icmp.c'; else $(CYGPATH_W) '$(srcdir)/icmp.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-icmp.Tpo $(DEPDIR)/pdnsd-icmp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='icmp.c' object='pdnsd-icmp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-icmp.obj `if test -f 'icmp.c'; then $(CYGPATH_W) 'icmp.c'; else $(CYGPATH_W) '$(srcdir)/icmp.c'; fi` + +pdnsd-list.o: list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-list.o -MD -MP -MF $(DEPDIR)/pdnsd-list.Tpo -c -o pdnsd-list.o `test -f 'list.c' || echo '$(srcdir)/'`list.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-list.Tpo $(DEPDIR)/pdnsd-list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='list.c' object='pdnsd-list.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-list.o `test -f 'list.c' || echo '$(srcdir)/'`list.c + +pdnsd-list.obj: list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-list.obj -MD -MP -MF $(DEPDIR)/pdnsd-list.Tpo -c -o pdnsd-list.obj `if test -f 'list.c'; then $(CYGPATH_W) 'list.c'; else $(CYGPATH_W) '$(srcdir)/list.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-list.Tpo $(DEPDIR)/pdnsd-list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='list.c' object='pdnsd-list.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-list.obj `if test -f 'list.c'; then $(CYGPATH_W) 'list.c'; else $(CYGPATH_W) '$(srcdir)/list.c'; fi` + +pdnsd-main.o: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-main.o -MD -MP -MF $(DEPDIR)/pdnsd-main.Tpo -c -o pdnsd-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-main.Tpo $(DEPDIR)/pdnsd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='pdnsd-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +pdnsd-main.obj: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-main.obj -MD -MP -MF $(DEPDIR)/pdnsd-main.Tpo -c -o pdnsd-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-main.Tpo $(DEPDIR)/pdnsd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='pdnsd-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +pdnsd-netdev.o: netdev.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-netdev.o -MD -MP -MF $(DEPDIR)/pdnsd-netdev.Tpo -c -o pdnsd-netdev.o `test -f 'netdev.c' || echo '$(srcdir)/'`netdev.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-netdev.Tpo $(DEPDIR)/pdnsd-netdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='netdev.c' object='pdnsd-netdev.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-netdev.o `test -f 'netdev.c' || echo '$(srcdir)/'`netdev.c + +pdnsd-netdev.obj: netdev.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-netdev.obj -MD -MP -MF $(DEPDIR)/pdnsd-netdev.Tpo -c -o pdnsd-netdev.obj `if test -f 'netdev.c'; then $(CYGPATH_W) 'netdev.c'; else $(CYGPATH_W) '$(srcdir)/netdev.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-netdev.Tpo $(DEPDIR)/pdnsd-netdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='netdev.c' object='pdnsd-netdev.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-netdev.obj `if test -f 'netdev.c'; then $(CYGPATH_W) 'netdev.c'; else $(CYGPATH_W) '$(srcdir)/netdev.c'; fi` + +pdnsd-rr_types.o: rr_types.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-rr_types.o -MD -MP -MF $(DEPDIR)/pdnsd-rr_types.Tpo -c -o pdnsd-rr_types.o `test -f 'rr_types.c' || echo '$(srcdir)/'`rr_types.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-rr_types.Tpo $(DEPDIR)/pdnsd-rr_types.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rr_types.c' object='pdnsd-rr_types.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-rr_types.o `test -f 'rr_types.c' || echo '$(srcdir)/'`rr_types.c + +pdnsd-rr_types.obj: rr_types.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-rr_types.obj -MD -MP -MF $(DEPDIR)/pdnsd-rr_types.Tpo -c -o pdnsd-rr_types.obj `if test -f 'rr_types.c'; then $(CYGPATH_W) 'rr_types.c'; else $(CYGPATH_W) '$(srcdir)/rr_types.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-rr_types.Tpo $(DEPDIR)/pdnsd-rr_types.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rr_types.c' object='pdnsd-rr_types.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-rr_types.obj `if test -f 'rr_types.c'; then $(CYGPATH_W) 'rr_types.c'; else $(CYGPATH_W) '$(srcdir)/rr_types.c'; fi` + +pdnsd-status.o: status.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-status.o -MD -MP -MF $(DEPDIR)/pdnsd-status.Tpo -c -o pdnsd-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-status.Tpo $(DEPDIR)/pdnsd-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='status.c' object='pdnsd-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +pdnsd-status.obj: status.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-status.obj -MD -MP -MF $(DEPDIR)/pdnsd-status.Tpo -c -o pdnsd-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-status.Tpo $(DEPDIR)/pdnsd-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='status.c' object='pdnsd-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +pdnsd-servers.o: servers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-servers.o -MD -MP -MF $(DEPDIR)/pdnsd-servers.Tpo -c -o pdnsd-servers.o `test -f 'servers.c' || echo '$(srcdir)/'`servers.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-servers.Tpo $(DEPDIR)/pdnsd-servers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='servers.c' object='pdnsd-servers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-servers.o `test -f 'servers.c' || echo '$(srcdir)/'`servers.c + +pdnsd-servers.obj: servers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-servers.obj -MD -MP -MF $(DEPDIR)/pdnsd-servers.Tpo -c -o pdnsd-servers.obj `if test -f 'servers.c'; then $(CYGPATH_W) 'servers.c'; else $(CYGPATH_W) '$(srcdir)/servers.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-servers.Tpo $(DEPDIR)/pdnsd-servers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='servers.c' object='pdnsd-servers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-servers.obj `if test -f 'servers.c'; then $(CYGPATH_W) 'servers.c'; else $(CYGPATH_W) '$(srcdir)/servers.c'; fi` + +pdnsd-thread.o: thread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-thread.o -MD -MP -MF $(DEPDIR)/pdnsd-thread.Tpo -c -o pdnsd-thread.o `test -f 'thread.c' || echo '$(srcdir)/'`thread.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-thread.Tpo $(DEPDIR)/pdnsd-thread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='thread.c' object='pdnsd-thread.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-thread.o `test -f 'thread.c' || echo '$(srcdir)/'`thread.c + +pdnsd-thread.obj: thread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-thread.obj -MD -MP -MF $(DEPDIR)/pdnsd-thread.Tpo -c -o pdnsd-thread.obj `if test -f 'thread.c'; then $(CYGPATH_W) 'thread.c'; else $(CYGPATH_W) '$(srcdir)/thread.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-thread.Tpo $(DEPDIR)/pdnsd-thread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='thread.c' object='pdnsd-thread.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-thread.obj `if test -f 'thread.c'; then $(CYGPATH_W) 'thread.c'; else $(CYGPATH_W) '$(srcdir)/thread.c'; fi` + +pdnsd-cache.o: cache.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-cache.o -MD -MP -MF $(DEPDIR)/pdnsd-cache.Tpo -c -o pdnsd-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-cache.Tpo $(DEPDIR)/pdnsd-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='pdnsd-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +pdnsd-cache.obj: cache.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-cache.obj -MD -MP -MF $(DEPDIR)/pdnsd-cache.Tpo -c -o pdnsd-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-cache.Tpo $(DEPDIR)/pdnsd-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='pdnsd-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` + +pdnsd-hash.o: hash.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-hash.o -MD -MP -MF $(DEPDIR)/pdnsd-hash.Tpo -c -o pdnsd-hash.o `test -f 'hash.c' || echo '$(srcdir)/'`hash.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-hash.Tpo $(DEPDIR)/pdnsd-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hash.c' object='pdnsd-hash.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-hash.o `test -f 'hash.c' || echo '$(srcdir)/'`hash.c + +pdnsd-hash.obj: hash.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-hash.obj -MD -MP -MF $(DEPDIR)/pdnsd-hash.Tpo -c -o pdnsd-hash.obj `if test -f 'hash.c'; then $(CYGPATH_W) 'hash.c'; else $(CYGPATH_W) '$(srcdir)/hash.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-hash.Tpo $(DEPDIR)/pdnsd-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hash.c' object='pdnsd-hash.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-hash.obj `if test -f 'hash.c'; then $(CYGPATH_W) 'hash.c'; else $(CYGPATH_W) '$(srcdir)/hash.c'; fi` + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic \ + clean-sbinPROGRAMS ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags tags-recursive uninstall uninstall-am \ + uninstall-sbinPROGRAMS + + +$(pdnsd_OBJECTS): rr_types.h + +rr_types.h: make_rr_types_h.pl rr_types.in + perl make_rr_types_h.pl rr_types.in > rr_types.h + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/cache.c b/app/src/main/jni/pdnsd/src/cache.c new file mode 100644 index 0000000..4fbb7fb --- /dev/null +++ b/app/src/main/jni/pdnsd/src/cache.c @@ -0,0 +1,2723 @@ +/* cache.c - Keep the dns caches. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cache.h" +#include "hash.h" +#include "conff.h" +#include "helpers.h" +#include "dns.h" +#include "error.h" +#include "debug.h" +#include "thread.h" +#include "ipvers.h" + + +/* A version identifier to prevent reading incompatible cache files */ +static const char cachverid[] = {'p','d','1','3'}; + +/* CACHE STRUCTURE CHANGES IN PDNSD 1.0.0 + * Prior to version 1.0.0, the cache was managed at domain granularity (all records of a domain were handled as a unit), + * which was suboptimal after the lean query feature and the additional record management were included. + * From 1.0.0 on, the cache management was switched to act with RR set granularity. The API of the cache handlers was + * slightly modified, in particular the rr_bucket_t was modified and some parameter list were changed. The cache + * file format had to be changed and is incompatible now. This means that post-1.0.0p1 versions will not read the cache + * files of older versions and vice versa. In addition, cache files from 1.0.0p5 on are incompatible to those of 1.0.0p1 + * to 1.0.0p4. Better delete them before upgrading. + * The "cent" lists common to old versions have vanished; the only access point to the cent's is the hash. + * However, there are now double linked rrset lists. Thus, rrs can be acces through the hash or through the rrset lists. + * The rrset list entries need some additional entries to manage the deletion from rrs lists as well as from the cents. + * + * Nearly all cache functions had to be changed significantly or even to be rewritten for that. Expect some beta time + * because of that. + * There are bonuses visible to the users resulting from this changes however: more consistent cache handling (under + * some circumstances, rrs could be in the cache more than once) and reduced memory requirements, as no rr needs + * to have stored its oname any more. There are more pointers however, and in some cases (CNAMES) the memory require- + * ments for some records may increase. The total should be lower, however. + * + * RRSET_L LIST STRUCTURE: + * The rrset_l rrset list is a simple double-linked list. The oldest entries are at the first positions, the list is sorted + * by age in descending order. Search is done only on insert. + * The rationale for this form is: + * - the purging operation needs to be fast (this way, the first records are the oldest and can easily be purged) + * - the append operation is common and needs to be fast (in normal operation, an appended record was just retrieved + * and therefore is the newest, so it can be appended at the end of the list without search. Only in the case of + * reading a disk cache file, searches are necessary) + * The rrset list is excusively used for purging purposes. + * + * THE DISK CACHE FILES: + * The disk cache file consists of cent's, i.e. structures for every known hostnames with a header and rrs attached to it. + * Therefore, the rr's are not ordered by their age and a search must be performed to insert the into the rr_l in the + * right positions. This operations has some costs (although not all too much), but the other way (rrs stored in order + * of their age and the cent headers separated from them), the rrs would need to be attached to the cent headers, which + * would be even more costly, also in means of disk space. + * + * CHANGES AFTER 1.0.0p1 + * In 1.0.0p5, the cache granularity was changed from rr level to rr set level. This was done because rfc2181 demands + * rr set consistency constraints on rr set level and if we are doing so we can as well save space (and eliminate some + * error-prone algorithms). + * + * CHANGES FOR 1.1.0p1 + * In this version, negative caching support was introduced. Following things were changed for that: + * - new members ts, ttl and flags in dns_cent_t and dns_file_t + * - new caching flag CF_NEGATIVE + * - all functions must accept and deal correctly with empty cents with DF_NEGATIVE set. + * - all functions must accept and deal correctly with empty rrsets with CF_NEGATIVE set. + */ + + +/* + * This is the size the memory cache may exceed the size of the permanent cache. + */ +#define MCSZ 10240 + +/* Some structs used for storing cache entries in a file. */ +typedef struct { + unsigned short rdlen; +/* data (with length rdlen) follows here;*/ +} rr_fbucket_t; + +typedef struct { + unsigned char tp; /* RR type */ + unsigned char num_rr; /* Number of records in RR set. */ + unsigned short flags; /* Flags for RR set. */ + time_t ttl; + time_t ts; +} __attribute__((packed)) +rr_fset_t; + +#if NRRTOT>255 +#warning "Number of cache-able RR types is greater than 255. This can give problems when saving the cache to file." +#endif + +typedef struct { + unsigned char qlen; /* Length of the domain name which follows after the struct. */ + unsigned char num_rrs; /* Number of RR-sets. */ + unsigned short flags; /* Flags for the whole cent. */ + unsigned char c_ns,c_soa; /* Number of trailing name elements in qname to use to find NS or SOA + records to add to the authority section of a response. */ + /* ttl and ts follow but only for negatively cached domains. */ + /* qname (with length qlen) follows here. */ +} __attribute__((packed)) +dns_file_t; + + +/* TTL and timestamp for negatively cached domains. */ +typedef struct { + time_t ttl; + time_t ts; +} __attribute__((packed)) +dom_fttlts_t; + +/* + * This has two modes: Normally, we have rrset, cent and idx filled in; + * for negatively cached cents, we have rrset set to NULL and idx set to -1. + */ +typedef struct rr_lent_s { + struct rr_lent_s *next; + struct rr_lent_s *prev; + rr_set_t *rrset; + dns_cent_t *cent; + int idx; /* This is the array index, not the type of the RR-set. */ +} rr_lent_t; + + +static rr_lent_t *rrset_l=NULL; +static rr_lent_t *rrset_l_tail=NULL; + +/* + * We do not count the hash table sizes here. Those are very small compared + * to the cache entries. + */ +static volatile long cache_size=0; +static volatile long ent_num=0; + +static volatile int cache_w_lock=0; +static volatile int cache_r_lock=0; + +pthread_mutex_t lock_mutex = PTHREAD_MUTEX_INITIALIZER; +/* + * These are condition variables for lock coordination, so that normal lock + * routines do not need to loop. Basically, a process wanting to acquire a lock + * tries first to lock, and if the lock is busy, sleeps on one of the conds. + * If the r lock count has gone to zero one process sleeping on the rw cond + * will be awankened. + * If the rw lock is lifted, either all threads waiting on the r lock or one + * thread waiting on the rw lock is/are awakened. This is determined by policy. + */ +pthread_cond_t rw_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t r_cond = PTHREAD_COND_INITIALIZER; + +/* This is to suspend the r lock to avoid lock contention by reading threads */ +static volatile int r_pend=0; +static volatile int rw_pend=0; +static volatile int r_susp=0; + +/* This threshold is used to temporarily suspend r locking to give rw locking + * a chance. */ +#define SUSP_THRESH(r_pend) (r_pend/2+2) + +/* + * This is set to 1 once the lock is intialized. This must happen before we get + * multiple threads. + */ +volatile short int use_cache_lock=0; + +/* + This is set to 0 while cache is read from disk. + This must be set to 1 before we start adding new entries. +*/ +static short int insert_sort=1; + + +#ifdef ALLOC_DEBUG +#define cache_free(ptr) { if (dbg) pdnsd_free(ptr); else free(ptr); } +#define cache_malloc(sz) ((dbg)?(pdnsd_malloc(sz)):(malloc(sz))) +#define cache_calloc(n,sz) ((dbg)?(pdnsd_calloc(n,sz)):(calloc(n,sz))) +#define cache_realloc(ptr,sz) ((dbg)?(pdnsd_realloc(ptr,sz)):(realloc(ptr,sz))) +#else +#define cache_free(ptr) {free(ptr);} +#define cache_malloc(sz) (malloc(sz)) +#define cache_calloc(n,sz) (calloc(n,sz)) +#define cache_realloc(ptr,sz) (realloc(ptr,sz)) +#endif + + +/* + * Prototypes for internal use + */ +static void purge_cache(long sz, int lazy); +static void del_cache_ent(dns_cent_t *cent,dns_hash_loc_t *loc); +static void remove_rrl(rr_lent_t *le DBGPARAM); + +/* + * Locking functions. + */ + +/* + * Lock/unlock cache for reading. Concurrent reads are allowed, while writes are forbidden. + * DO NOT MIX THE LOCK TYPES UP WHEN LOCKING/UNLOCKING! + * + * We use a mutex to lock the access to the locks ;-). + * This is because we do not allow read and write to interfere (for which a normal mutex would be + * fine), but we also want to allow concurrent reads. + * We use condition variables, and readlock contention protection. + */ +static void lock_cache_r(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + r_pend++; + while(((rw_pend>SUSP_THRESH(r_pend))?(r_susp=1):r_susp) || cache_w_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&r_cond, &lock_mutex); + } + cache_r_lock++; + r_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +static void unlock_cache_r(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + if (cache_r_lock>0) + cache_r_lock--; + /* wakeup threads waiting to write */ + if (!cache_r_lock) + pthread_cond_signal(&rw_cond); + pthread_mutex_unlock(&lock_mutex); +} + +/* + * Lock/unlock cache for reading and writing. Concurrent reads and writes are forbidden. + * Do this only if you actually modify the cache. + * DO NOT MIX THE LOCK TYPES UP WHEN LOCKING/UNLOCKING! + * (cant say it often enough) + */ +static void lock_cache_rw(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&rw_cond, &lock_mutex); + } + cache_w_lock=1; + rw_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +/* Lock cache for reading and writing, or time out after tm seconds. */ +static int timedlock_cache_rw(int tm) +{ + int retval=0; + struct timeval now; + struct timespec timeout; + + if (!use_cache_lock) + return 0; + pthread_mutex_lock(&lock_mutex); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + tm; + timeout.tv_nsec = now.tv_usec * 1000; + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + if(pthread_cond_timedwait(&rw_cond, &lock_mutex, &timeout) == ETIMEDOUT) + goto cleanup_return; + } + cache_w_lock=1; + retval=1; + cleanup_return: + rw_pend--; + pthread_mutex_unlock(&lock_mutex); + return retval; +} + +static void unlock_cache_rw(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + cache_w_lock=0; + /* always reset r suspension (r locking code will set it again) */ + r_susp=0; + /* wakeup threads waiting to read or write */ + if (r_pend==0 || rw_pend>SUSP_THRESH(r_pend)) + pthread_cond_signal(&rw_cond); /* schedule another rw proc */ + else + pthread_cond_broadcast(&r_cond); /* let 'em all read */ + pthread_mutex_unlock(&lock_mutex); +} + + +/* + If there are other threads waiting to read from or write to + the cache, give up the read/write lock on the cache to give another + thread a chance; then try to get the lock back again. + This can be called regularly during a process that takes + a lot of processor time but has low priority, in order to improve + overall responsiveness. +*/ +static void yield_lock_cache_rw() +{ + if (!use_cache_lock || (!r_pend && !rw_pend)) + return; + + /* Give up the lock */ + pthread_mutex_lock(&lock_mutex); + cache_w_lock=0; + /* always reset r suspension (r locking code will set it again) */ + r_susp=0; + /* wakeup threads waiting to read or write */ + if (r_pend==0 || rw_pend>SUSP_THRESH(r_pend)) + pthread_cond_signal(&rw_cond); /* schedule another rw proc */ + else + pthread_cond_broadcast(&r_cond); /* let 'em all read */ + pthread_mutex_unlock(&lock_mutex); + + usleep_r(1000); + + /* Now try to get the lock back again */ + pthread_mutex_lock(&lock_mutex); + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&rw_cond, &lock_mutex); + } + cache_w_lock=1; + rw_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +/* These are a special version of the ordinary read lock functions. The lock "soft" to avoid deadlocks: they will give up + * after a certain number of bad trials. You have to check the exit status though. + * To avoid blocking mutexes, we cannot use condition variables here. Never mind, these are only used on + * exit. */ +static int softlock_cache_r(void) +{ + if (!use_cache_lock) + return 0; + { + int lk=0,tr=0; + + for(;;) { + if (!softlock_mutex(&lock_mutex)) + return 0; + if(!cache_w_lock) { + lk=1; + cache_r_lock++; + } + pthread_mutex_unlock(&lock_mutex); + if (lk) break; + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(1000); /*give contol back to the scheduler instead of hammering the lock close*/ + } + } + return 1; +} + +/* On unlocking, we do not wake others. We are about to exit! */ +static int softunlock_cache_r(void) +{ + if (!use_cache_lock) + return 0; + if (!softlock_mutex(&lock_mutex)) + return 0; + if (cache_r_lock>0) + cache_r_lock--; + pthread_mutex_unlock(&lock_mutex); + return 1; +} + +static int softlock_cache_rw(void) +{ + if (!use_cache_lock) + return 0; + { + int lk=0,tr=0; + + for(;;) { + if (!softlock_mutex(&lock_mutex)) + return 0; + if (!(cache_w_lock || cache_r_lock)) { + lk=1; + cache_w_lock=1; + } + pthread_mutex_unlock(&lock_mutex); + if(lk) break; + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(1000); /*give contol back to the scheduler instead of hammering the lock close*/ + } + } + return 1; +} + +static int softunlock_cache_rw(void) +{ + if (!use_cache_lock) + return 0; + if (!softlock_mutex(&lock_mutex)) + return 0; + cache_w_lock=0; + pthread_mutex_unlock(&lock_mutex); + return 1; +} + +/* + * Serial numbers: Serial numbers are used when additional records are added to the cache: serial numbers are unique to each + * query, so we can determine whether data was added by the query just executed (records can coexist) or not (records must + * be replaced). A serial of 0 is special and will not be used by any query. All records added added authoritatively (as + * chunk) or read from a file can have no query in process and therefore have serial 0, which is != any other serial. + */ +#if 0 +unsigned long l_serial=1; + +unsigned long get_serial() +{ + unsigned long rv; + lock_cache_rw(); + rv=l_serial++; + unlock_cache_rw(); + return rv; +} +#endif + +/* + * Cache/cent handlers + */ + +/* Initialize the cache. Call only once. */ +#if 0 +void init_cache() +{ + mk_hash_ctable(); + mk_dns_hash(); +} +#endif + +/* Initialize the cache lock. Call only once. */ +/* This is now defined as an inline function in cache.h */ +#if 0 +void init_cache_lock() +{ + + use_cache_lock=1; +} +#endif + +/* Empty the cache, freeing all entries that match the include/exclude list. */ +int empty_cache(slist_array sla) +{ + int i; + + /* Wait at most 60 seconds to obtain a lock. */ + if(!timedlock_cache_rw(60)) + return 0; + + for(i=0; ; ) { + if(sla) + free_dns_hash_selected(i,sla); + else + free_dns_hash_bucket(i); + if(++i>=HASH_NUM_BUCKETS) + break; + /* Give another thread a chance */ + yield_lock_cache_rw(); + } + + unlock_cache_rw(); + return 1; +} + +/* Delete the cache. Call only once */ +void destroy_cache() +{ + /* lock the cache, in case that any thread is still accessing. */ + if(!softlock_cache_rw()) { + log_error("Lock failed; could not destroy cache on exit."); + return; + } + free_dns_hash(); +#if DEBUG>0 + if(ent_num || cache_size) { + DEBUG_MSG("After destroying cache, %ld entries (%ld bytes) remaining.\n",ent_num,cache_size); + } +#endif + +#if 0 +#if (TARGET!=TARGET_LINUX) + /* under Linux, this frees no resources but may hang on a crash */ + pthread_mutex_destroy(&lock_mutex); + pthread_cond_destroy(&rw_cond); + pthread_cond_destroy(&r_cond); +#endif +#endif +} + +/* Make a flag value for a dns_cent_t (dns cache entry) from a server record */ +/* Now defined as inline function in cache.h */ +#if 0 +unsigned int mk_flag_val(servparm_t *server) +{ + unsigned int fl=0; + if (!server->purge_cache) + fl|=CF_NOPURGE; + if (server->nocache) + fl|=CF_NOCACHE; + if (server->rootserver) + fl|=CF_ROOTSERV; + return fl; +} +#endif + +/* Initialize a dns cache record (dns_cent_t) with the query name (in + * transport format), a flag value, a timestamp indicating + * the time the query was done, and a TTL. The timestamp and TTL + * are only used if DF_NEGATIVE is set in the flags. Otherwise, + * the timestamps of the individual records are used. DF_NEGATIVE + * is used for whole-domain negative caching. + * By convention, ttl and ts should be set to 0, unless the + * DF_NEGATIVE bit is set. */ +int init_cent(dns_cent_t *cent, const unsigned char *qname, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + int i; + size_t namesz=rhnlen(qname); + + cent->qname=cache_malloc(namesz); + if (cent->qname == NULL) + return 0; + memcpy(cent->qname,qname,namesz); + cent->cs=sizeof(dns_cent_t)+namesz; + cent->num_rrs=0; + cent->flags=flags; + if(flags&DF_NEGATIVE) { + cent->neg.lent=NULL; + cent->neg.ttl=ttl; + cent->neg.ts=ts; + } + else { + for(i=0; irr.rrmu[i]=NULL; + cent->rr.rrext=NULL; + } + cent->c_ns=cundef; + cent->c_soa=cundef; + return 1; +} + +/* + * Create a rr record holder using the given values. + */ +static rr_bucket_t *create_rr(unsigned dlen, void *data DBGPARAM) +{ + rr_bucket_t *rrb; + rrb=(rr_bucket_t *)cache_malloc(sizeof(rr_bucket_t)+dlen); + if (rrb == NULL) + return NULL; + rrb->next=NULL; + + rrb->rdlen=dlen; + memcpy(rrb->data,data,dlen); + return rrb; +} + +/* + * Adds an empty rrset_t with the requested data to a cent. This is exactly what you need to + * do to create a negatively cached cent. + */ +static int add_cent_rrset_by_index(dns_cent_t *cent, unsigned int idx, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + rr_set_t **rrext, **rrsetpa, *rrset; + + /* If we add a rrset, even a negative one, the domain is not negative any more. */ + if (cent->flags&DF_NEGATIVE) { + int i; + /* need to remove the cent from the lent list. */ + if (cent->neg.lent) + remove_rrl(cent->neg.lent DBGARG); + cent->flags &= ~DF_NEGATIVE; + for(i=0; irr.rrmu[i]=NULL; + cent->rr.rrext=NULL; + } + + if(idx < NRRMU) + rrsetpa = ¢->rr.rrmu[idx]; + else { + idx -= NRRMU; + PDNSD_ASSERT(idx < NRREXT, "add_cent_rrset_by_index: rr-set index out of range"); + rrext = cent->rr.rrext; + if(!rrext) { + int i; + cent->rr.rrext = rrext = cache_malloc(sizeof(rr_set_t*)*NRREXT); + if(!rrext) + return 0; + for(i=0; ics += sizeof(rr_set_t*)*NRREXT; + } + rrsetpa = &rrext[idx]; + } + +#if 0 + if(*rrsetpa) del_rrset(*rrsetpa); +#endif + *rrsetpa = rrset = cache_malloc(sizeof(rr_set_t)); + if (!rrset) + return 0; + rrset->lent=NULL; + rrset->ttl=ttl; + rrset->ts=ts; + rrset->flags=flags; + rrset->rrs=NULL; + cent->cs += sizeof(rr_set_t); + ++cent->num_rrs; + return 1; +} + +int add_cent_rrset_by_type(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + int tpi = type - T_MIN; + + PDNSD_ASSERT(tpi>=0 && tpinext=rrset->rrs; + rrset->rrs=rr; + } + else { + /* append at the end */ + rr->next=(*rtail)->next; + (*rtail)->next=rr; + } + if(rtail) *rtail=rr; + cent->cs += sizeof(rr_bucket_t)+rr->rdlen; +#if DEBUG>0 + if(debug_p) { + rrset = RRARR_INDEX(cent,idx); + if (rrset->flags&CF_NEGATIVE) { + char cflagstr[CFLAGSTRLEN]; + DEBUG_MSG("Tried to add rr to a rrset with CF_NEGATIVE set! flags=%s\n",cflags2str(rrset->flags,cflagstr)); + } + } +#endif + return 1; + + cleanup_return: + free_rr(*rr); + free(rr); + return 0; +} + + +/* Add an rr to a cache entry, giving the ttl, the data length, the rr type + * and a pointer to the data. A record is allocated, and the data is copied into + * it. Do this for all rrs in a cache entry. + * The return value will be 1 in case of success, or 0 in case of a memory allocation + * problem. + */ +int add_cent_rr(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags, + unsigned dlen, void *data DBGPARAM) +{ + int tpi; + unsigned int idx; + rr_set_t *rrset; + rr_bucket_t *rtail, *rrb; + + if ((cent->flags&DF_LOCAL) && !(flags&CF_LOCAL)) + return 1; /* ignore. Local has precedence. */ + + tpi = type - T_MIN; + PDNSD_ASSERT(tpi>=0 && tpittl) + /* The ttl timestamps should be identical. + In case they are not, we will use the smallest one. */ + rrset->ttl= ttl; + + /* OK, some stupid nameservers feel inclined to return the same address twice. Grmbl... */ + rrb=rrset->rrs; + while (rrb) { + if (rrb->rdlen==dlen && memcmp(rrb->data,data,dlen)==0) + return 1; + rtail=rrb; + rrb=rrb->next; + } + } + return add_cent_rr_int(cent,idx,ttl,ts,flags,dlen,data,&rtail DBGARG); +} + +/* Free a complete rrset including all memory. Returns the size of the memory freed */ +int del_rrset(rr_set_t *rrs DBGPARAM) +{ + int rv=sizeof(rr_set_t); + rr_bucket_t *rrb,*rrn; + + if(rrs->lent) remove_rrl(rrs->lent DBGARG); + rrb=rrs->rrs; + while (rrb) { + rv+=sizeof(rr_bucket_t)+rrb->rdlen; + rrn=rrb->next; + free_rr(*rrb); + cache_free(rrb); + rrb=rrn; + } + cache_free(rrs); + return rv; +} + +/* Remove a complete rrset from a cent, freeing the memory. + The second argument should be an RR-set array index, not an RR type! + Returns the size of the memory freed */ +static int del_cent_rrset_by_index(dns_cent_t *cent, int i DBGPARAM) +{ + int rv=0; + rr_set_t **rrspa = RRARR_INDEX_PA_TESTEXT(cent,i); + + if(rrspa) { + rr_set_t *rrs = *rrspa; + if(rrs) { + rv= del_rrset(rrs DBGARG); + *rrspa=NULL; + --cent->num_rrs; + cent->cs -= rv; + cent->flags &= ~DF_AUTH; + } + } + return rv; +} + +#if 0 +static int del_cent_rrset_by_type(dns_cent_t *cent, int type DBGPARAM) +{ + return del_cent_rrset_by_index(cent, rrlkuptab[type-T_MIN] DBGARG); +} +#endif + +#if 0 +/* Free the pointers contained in an rr record. If the rr record is on the heap, + * don't forget to delete itself. This is done extra mainly for extensibility + * -- This is not here any more. The definition is actually an empty macro in + * cache.h. + */ +void free_rr(rr_bucket_t rr) +{ +} +#endif + +/* Free all data referred by a cache entry. */ +void free_cent(dns_cent_t *cent DBGPARAM) +{ + cache_free(cent->qname); + if(cent->flags&DF_NEGATIVE) { + if(cent->neg.lent) + remove_rrl(cent->neg.lent DBGARG); + } + else { + int i; + for (i=0; irr.rrmu[i]; + if (rrs) del_rrset(rrs DBG0); + } + { + rr_set_t **rrext = cent->rr.rrext; + if(rrext) { + for(i=0; iflags&DF_NEGATIVE)) { + for (i=0; irr.rrmu[i]; + if (rrs) { + cent->cs -= del_rrset(rrs DBG0); + /* cent->rr.rrmu[i]=NULL; */ + } + } + { + rr_set_t **rrext = cent->rr.rrext; + if(rrext) { + for(i=0; ics -= del_rrset(rrs DBG0); + } + cache_free(rrext); + /* cent->rr.rrext=NULL; */ + cent->cs -= sizeof(rr_set_t*)*NRREXT; + } + } + cent->num_rrs=0; + cent->flags |= DF_NEGATIVE; + cent->neg.lent=NULL; + } + + cent->neg.ttl=ttl; + cent->neg.ts=ts; +} + +inline static time_t get_rrlent_ts(rr_lent_t *le) +{ + return (le->rrset)?(le->rrset->ts):(le->cent->neg.ts); +} + +/* insert a rrset into the rr_l list. This modifies the rr_set_t if rrs is not NULL! + * The rrset address needs to be constant afterwards. + * idx is the internally used RR-set index, not the RR type! + * Call with locks applied. */ +static int insert_rrl(rr_set_t *rrs, dns_cent_t *cent, int idx) +{ + time_t ts; + rr_lent_t *le,*ne; + + /* No need to add local records to the rr_l list, because purge_cache() ignores them anyway. */ + if((rrs && (rrs->flags&CF_LOCAL)) || (cent->flags&DF_LOCAL)) + return 1; + + if (!(ne=malloc(sizeof(rr_lent_t)))) + return 0; + ne->rrset=rrs; + ne->cent=cent; + ne->idx=idx; + ne->next=NULL; + ne->prev=NULL; + + if(insert_sort) { + /* Since the append at the and is a very common case (and we want this case to be fast), we search back-to-forth. + * Since rr_l is a list and we don't really have fast access to all elements, we do not perform an advanced algorithm + * like binary search.*/ + ts=get_rrlent_ts(ne); + le=rrset_l_tail; + while (le) { + if (ts>=get_rrlent_ts(le)) goto found; + le=le->prev; + } + /* not found, so it needs to be inserted at the start of the list. */ + ne->next=rrset_l; + if (rrset_l) + rrset_l->prev=ne; + else + rrset_l_tail=ne; + rrset_l=ne; + goto finish; + found: + ne->next=le->next; + ne->prev=le; + if (le->next) + le->next->prev=ne; + else + rrset_l_tail=ne; + le->next=ne; + finish:; + } + else { + /* simply append at the end, sorting will be done later with a more efficient algorithm. */ + ne->prev=rrset_l_tail; + if(rrset_l_tail) + rrset_l_tail->next=ne; + else + rrset_l=ne; + rrset_l_tail=ne; + } + + if (rrs) + rrs->lent=ne; + else + cent->neg.lent=ne; + + return 1; +} + +/* Remove a rr from the rr_l list. Call with locks applied. */ +static void remove_rrl(rr_lent_t *le DBGPARAM) +{ + rr_lent_t *next=le->next,*prev=le->prev; + if (next) + next->prev=prev; + else + rrset_l_tail=prev; + if (prev) + prev->next=next; + else + rrset_l=next; + cache_free(le); +} + + +/* Merge two sorted rr_l lists to make a larger sorted list. + The lists are sorted according to increasing time-stamp. + The back links are ignored, these must be fixed using a separate pass. +*/ +static rr_lent_t *listmerge(rr_lent_t *p, rr_lent_t *q) +{ + + if(!p) + return q; + else if(!q) + return p; + else { + rr_lent_t *l=NULL, **s= &l; + + for(;;) { + if(get_rrlent_ts(p) <= get_rrlent_ts(q)) { + *s= p; + s= &p->next; + p= *s; + if(!p) { + *s= q; + break; + } + } + else { /* get_rrlent_ts(p) > get_rrlent_ts(q) */ + *s= q; + s= &q->next; + q= *s; + if(!q) { + *s= p; + break; + } + } + } + + return l; + } +} + +/* Sort the rr_l list using merge sort, which can be more efficient than insertion sort used by rr_insert(). + This algorithm is adapted from the GNU C++ STL implementation for list containers. + Call with locks applied. + Written by Paul Rombouts. +*/ +static void sort_rrl() +{ + /* Do nothing unless the list has length >= 2. */ + if(rrset_l && rrset_l->next) { + /* First sort the list ignoring the back links, these will be fixed later. */ +# define NTMPSORT 32 + /* Because we use an array of fixed length, the length of the list we can sort + is bounded by pow(2,NTMPSORT)-1. */ + rr_lent_t *tmp[NTMPSORT]; /* tmp[i] will either be NULL or point to a sorted list of length pow(2,i). */ + rr_lent_t **fill= tmp, **end=tmp+NTMPSORT, **counter; + rr_lent_t *rem= rrset_l, *carry; + + do { + carry=rem; rem=rem->next; + carry->next=NULL; + for(counter = tmp; counter!=fill && *counter!=NULL; ++counter) { + carry=listmerge(*counter,carry); + *counter=NULL; + } + + PDNSD_ASSERT(counter!=end, "sort_rrl: tmp array overflowed"); + + *counter=carry; + + if(counter==fill) ++fill; + } + while(rem); + + /* Merge together all the remaining list fragments contained in array tmp. */ + carry= tmp[0]; + counter= tmp; + while(++counter!=fill) + carry=listmerge(*counter,carry); + + rrset_l= carry; + + { + /* Restore the backward links. */ + rr_lent_t *p,*q=NULL; + for(p=rrset_l; p; p=p->next) {p->prev=q; q=p;} + rrset_l_tail=q; + } + } +} + + +/* Copy a rr_bucket_t into newly allocated memory */ +inline static rr_bucket_t *copy_rr(rr_bucket_t *rr DBGPARAM) +{ + rr_bucket_t *rrn; + rrn=cache_malloc(sizeof(rr_bucket_t)+rr->rdlen); + if (rrn == NULL) + return NULL; + memcpy(rrn,rr,sizeof(rr_bucket_t)+rr->rdlen); + rrn->next=NULL; + return rrn; +} + + +/* Copy an RR set into newly allocated memory */ +static rr_set_t *copy_rrset(rr_set_t *rrset DBGPARAM) +{ + rr_set_t *rrsc=cache_malloc(sizeof(rr_set_t)); + rr_bucket_t *rr,**rrp; + if (rrsc) { + *rrsc=*rrset; + rrsc->lent=NULL; + rrp=&rrsc->rrs; + rr=rrset->rrs; + while(rr) { + rr_bucket_t *rrc=copy_rr(rr DBGARG); + *rrp=rrc; + if (!rrc) goto cleanup_return; + rrp=&rrc->next; + rr=rr->next; + } + } + return rrsc; + +cleanup_return: + del_rrset(rrsc DBG0); + return NULL; +} + + +/* Copy a cache entry into newly allocated memory */ +dns_cent_t *copy_cent(dns_cent_t *cent DBGPARAM) +{ + dns_cent_t *copy; + + /* + * We do not debug cache internals with it, as mallocs seem to be + * "lost" when they enter the cache for a longer time. + */ + if (!(copy=cache_malloc(sizeof(dns_cent_t)))) + return NULL; + + { + /* copy the name */ + size_t namesz=rhnlen(cent->qname); + if (!(copy->qname=cache_malloc(namesz))) + goto free_return_null; + + memcpy(copy->qname,cent->qname,namesz); + } + copy->cs= cent->cs; + copy->num_rrs= cent->num_rrs; + copy->flags= cent->flags; + copy->c_ns = cent->c_ns; + copy->c_soa= cent->c_soa; + if(cent->flags&DF_NEGATIVE) { + copy->neg.lent=NULL; + copy->neg.ttl= cent->neg.ttl; + copy->neg.ts = cent->neg.ts; + } + else { + int i, ilim; + for (i=0; irr.rrmu[i]=NULL; + copy->rr.rrext=NULL; + + ilim = NRRMU; + if(cent->rr.rrext) { + rr_set_t **rrextc; + ilim = NRRTOT; + copy->rr.rrext = rrextc = cache_malloc(sizeof(rr_set_t*)*NRREXT); + if(!rrextc) goto free_cent_return_null; + + for (i=0; ilent=NULL; + rrp=&rrsc->rrs; + rr=rrset->rrs; + while(rr) { + rr_bucket_t *rrc=copy_rr(rr DBGARG); + *rrp=rrc; + if (!rrc) goto free_cent_return_null; + rrp=&rrc->next; + rr=rr->next; + } + } + } + } + return copy; + + free_cent_return_null: + free_cent(copy DBGARG); + free_return_null: + cache_free(copy); + return NULL; +} + +/* + * Remove all timed out entries of the RR set with the given index. + * idx is the internally used RR-set index, not the RR type! + * Follow some rules based on flags etc. + * This will either delete the whole rrset, or will leave it as a whole (RFC2181 seems to + * go in that direction) + * This was pretty large once upon a time ;-), but now, since we operate in rrsets, was + * shrunk drastically. + * If test is zero and the record is in the cache, we need rw-locks applied. + * If test is nonzero, nothing will actually be deleted. + * Substracts the size of the freed memory from cache_size (if test is zero). + * Returns 1 if the rrset has been (or would have been) deleted. + */ +static int purge_rrset(dns_cent_t *cent, int idx, int test) +{ + rr_set_t *rrs= RRARR_INDEX_TESTEXT(cent,idx); + if (rrs && !(rrs->flags&CF_NOPURGE || rrs->flags&CF_LOCAL) && timedout(rrs)) { + /* well, it must go. */ + if(!test) + cache_size -= del_cent_rrset_by_index(cent,idx DBG0); + return 1; + } + return 0; +} + +/* + Remove all timed out entries of alls RR sets of a cache entry. + The test flag works the same as in purge_rrset(). + Substracts the size of the freed memory from cache_size, just as purge_rrset(). + *numrrsrem is set to the number of remaining RR sets (or the number that would have remained). + Returns the number of items (RR sets or RR set arrays) that have been (or would have been) deleted. +*/ +static int purge_all_rrsets(dns_cent_t *cent, int test, int *numrrsrem) +{ + int rv=0, numrrs=0, numrrext=0; + + if(!(cent->flags&DF_NEGATIVE)) { + int i, ilim= RRARR_LEN(cent); + for(i=0; iflags&CF_NOPURGE || rrs->flags&CF_LOCAL) && timedout(rrs)) { + /* well, it must go. */ + if(!test) + cache_size -= del_cent_rrset_by_index(cent, i DBG0); + ++rv; + } + else { + ++numrrs; + if(i>=NRRMU) ++numrrext; + } + } + } + + /* If the array of less frequently used RRs has become empty, free it. */ + if(cent->rr.rrext && numrrext==0) { + if(!test) { + cache_free(cent->rr.rrext); + cent->rr.rrext=NULL; + cent->cs -= sizeof(rr_set_t*)*NRREXT; + cache_size -= sizeof(rr_set_t*)*NRREXT; + } + ++rv; + } + } + + if(numrrsrem) *numrrsrem=numrrs; + return rv; +} + + +/* + * Purge a cent, deleting timed-out rrs (following the constraints noted in "purge_rrset"). + * Since the cent may actually become empty and be deleted, you may not use it after this call until + * you refetch its address from the hash (if it is still there). + * If test is zero and the record is in the cache, we need rw-locks applied. + * If test is nonzero, nothing will actually be deleted. + * Substracts the size of the freed memory from cache_size (if test is zero). + * If delete is nonzero and the cent was purged empty and no longer needed, it is removed from the cache. + * Returns -1 if the cent was (or would have been) completely removed, + * otherwise returns the number of items that were (or would have been) deleted. + */ +static int purge_cent(dns_cent_t *cent, int delete, int test) +{ + int npurge, numrrs; + + npurge = purge_all_rrsets(cent,test, &numrrs); + + /* If the cache entry was purged empty, delete it from the cache. */ + if (delete && numrrs==0 + && (!(cent->flags&DF_NEGATIVE) || + (!(cent->flags&DF_LOCAL) && timedout_nxdom(cent)))) + { + if(!test) + del_cache_ent(cent,NULL); /* this will subtract the cent's left size from cache_size */ + return -1; + } + + if(!(cent->flags&DF_LOCAL)) { + /* Set stale references to NS or SOA records back to undefined. */ + unsigned scnt=rhnsegcnt(cent->qname); + if(cent->c_ns!=cundef) { + rr_set_t *rrset=NULL; + if(cent->c_ns==scnt) + rrset=getrrset_NS(cent); + else if(cent->c_nsqname,scnt-cent->c_ns),NULL); + if(ce) rrset=getrrset_NS(ce); + } + if(!rrset || !rrset->rrs || (!(rrset->flags&CF_LOCAL) && timedout(rrset))) { + if(!test) + cent->c_ns=cundef; + ++npurge; + } + } + if(cent->c_soa!=cundef) { + rr_set_t *rrset=NULL; + if(cent->c_soa==scnt) + rrset=getrrset_SOA(cent); + else if(cent->c_soaqname,scnt-cent->c_soa),NULL); + if(ce) rrset=getrrset_SOA(ce); + } + if(!rrset || !rrset->rrs || (!(rrset->flags&CF_LOCAL) && timedout(rrset))) { + if(!test) + cent->c_soa=cundef; + ++npurge; + } + } + } + + return npurge; +} + +/* + * Bring cache to a size below or equal the cache size limit (sz). There are two strategies: + * - for cached sets with CF_NOPURGE not set: delete if timed out + * - additional: delete oldest sets. + */ +static void purge_cache(long sz, int lazy) +{ + rr_lent_t *le; + + /* Walk the cache list from the oldest entries to the newest, deleting timed-out + * records. + * XXX: We walk the list a second time if this did not free up enough space - this + * should be done better. */ + le=rrset_l; + while (le && (!lazy || cache_size>sz)) { + /* Note by Paul Rombouts: + * If data integrity is ensured, at most one node is removed from the rrset_l + * per iteration, and this node is the one referenced by le. */ + rr_lent_t *next=le->next; + if (!((le->rrset && (le->rrset->flags&CF_LOCAL)) || + (le->cent->flags&DF_LOCAL))) { + dns_cent_t *ce = le->cent; + if (le->rrset) + purge_rrset(ce, le->idx,0); + /* Side effect: if purge_rrset called del_cent_rrset then le has been freed. + * ce, however, is still guaranteed to be valid. */ + if (ce->num_rrs==0 && (!(ce->flags&DF_NEGATIVE) || + (!(ce->flags&DF_LOCAL) && timedout_nxdom(ce)))) + del_cache_ent(ce,NULL); + } + le=next; + } + if (cache_size<=sz) + return; + + /* we are still above the desired cache size. Well, delete records from the oldest to + * the newest. This is the case where nopurge records are deleted anyway. Only local + * records are kept in any case.*/ + if(!insert_sort) { + sort_rrl(); + insert_sort=1; /* use insertion sort from now on */ + } + + le=rrset_l; + while (le && cache_size>sz) { + rr_lent_t *next=le->next; + if (!((le->rrset && (le->rrset->flags&CF_LOCAL)) || + (le->cent->flags&DF_LOCAL))) { + dns_cent_t *ce = le->cent; + if (le->rrset) + cache_size -= del_cent_rrset_by_index(ce, le->idx DBG0); + /* this will also delete negative cache entries */ + if (ce->num_rrs==0) + del_cache_ent(ce,NULL); + } + le=next; + } +} + +#define log_warn_read_error(f,item) \ + log_warn("%s encountered while reading %s from disk cache file.", \ + ferror(f)?"Error":feof(f)?"EOF":"Incomplete item",item) + +/* + * Load cache from disk and rebuild the hash tables. + */ +void read_disk_cache() +{ + /* The locks are done when we add items. */ + dns_cent_t ce; + int dtsz=512; + unsigned char *data; + unsigned long cnt; + FILE *f; + + char path[strlen(global.cache_dir)+sizeof("/pdnsd.cache")]; + + stpcpy(stpcpy(path,global.cache_dir),"/pdnsd.cache"); + + if (!(f=fopen(path,"r"))) { + log_warn("Could not open disk cache file %s: %s",path,strerror(errno)); + return; + } + + if (!(data = malloc(dtsz))) { + goto fclose_exit; + } + + /* Don't use insertion sort while reading caches entries from disk, because this can be + noticeably inefficient with large cache files. + Entries are simply appended at the end of the rr_l list. + The rr_l list is sorted using a more efficient merge sort after we are done reading. + */ + insert_sort=0; + + { + unsigned nb; + char buf[sizeof(cachverid)]; + + /* check cache version identifier */ + nb=fread(buf,1,sizeof(cachverid),f); + if (nb!=sizeof(cachverid)) { + /* Don't complain about empty files */ + if(nb!=0 || !feof(f)) { + log_warn_read_error(f,"cache version identifier"); + } + goto free_data_fclose; + } + if(memcmp(buf,cachverid,sizeof(cachverid))) { + log_warn("Cache file %s ignored because of incompatible version identifier",path); + goto free_data_fclose; + } + } + + if (fread(&cnt,sizeof(cnt),1,f)!=1) { + log_warn_read_error(f,"entry count"); + goto free_data_fclose; + } + + for(;cnt>0;--cnt) { + dns_file_t fe; + dom_fttlts_t fttlts = {0,0}; + unsigned char nb[256]; + unsigned num_rrs; + unsigned char prevtp; + if (fread(&fe,sizeof(fe),1,f)!=1) { + log_warn_read_error(f,"cache entry header"); + goto free_data_fclose; + } + if(fe.flags&DF_NEGATIVE) { + if (fread(&fttlts,sizeof(fttlts),1,f)!=1) { + log_warn_read_error(f,"cache TTL and timestamp"); + goto free_data_fclose; + } + } + if (fe.qlen) { + int i; + /* Because of its type qlen should be <=255. */ + if (fread(nb,fe.qlen,1,f)!=1) { + log_warn_read_error(f,"domain name"); + goto free_data_fclose; + } + for(i=0;i63 || (i += lb+1)>fe.qlen) { + log_warn("Invalid domain name encountered while reading disk cache file."); + goto free_data_fclose; + } + } + } + nb[fe.qlen]='\0'; + if (!init_cent(&ce, nb, fttlts.ttl, fttlts.ts, fe.flags DBG0)) { + goto free_data_fclose_exit; + } + ce.c_ns=fe.c_ns; ce.c_soa=fe.c_soa; + + /* now, read the rr's */ + prevtp=0; + for (num_rrs=fe.num_rrs;num_rrs;--num_rrs) { + rr_fset_t sh; + unsigned num_rr; + if (fread(&sh,sizeof(sh),1,f)!=1) { + log_warn_read_error(f,"rr header"); + goto free_cent_data_fclose; + } + if(PDNSD_NOT_CACHED_TYPE(sh.tp)) { + log_warn("Invalid rr type encountered while reading disk cache file."); + goto free_data_fclose; + } + if(sh.tp<=prevtp) { + log_warn("Unexpected rr type encountered (not in strict ascending order) while reading disk cache file."); + goto free_data_fclose; + } + prevtp=sh.tp; + /* Add the rrset header in any case (needed for negative caching) */ + if(!add_cent_rrset_by_type(&ce, sh.tp, sh.ttl, sh.ts, sh.flags DBG0)) { + goto free_cent_data_fclose_exit; + } + for (num_rr=sh.num_rr;num_rr;--num_rr) { + rr_fbucket_t rr; + if (fread(&rr,sizeof(rr),1,f)!=1) { + log_warn_read_error(f,"rr data length"); + goto free_cent_data_fclose; + } + if (rr.rdlen>dtsz) { + unsigned char *tmp; + dtsz=rr.rdlen; + tmp=realloc(data,dtsz); + if (!tmp) { + goto free_cent_data_fclose_exit; + } + data=tmp; + } + if (rr.rdlen && fread(data,rr.rdlen,1,f)!=1) { + log_warn_read_error(f,"rr data"); + goto free_cent_data_fclose; + } + if (!add_cent_rr(&ce,sh.tp,sh.ttl,sh.ts,sh.flags,rr.rdlen,data DBG0)) { + goto free_cent_data_fclose_exit; + } + } + } + add_cache(&ce); + free_cent(&ce DBG0); + } +#ifdef DEBUG_HASH + free(data); + fclose(f); + dumphash(); + goto sort_return; +#else + goto free_data_fclose; +#endif + + free_cent_data_fclose: + free_cent(&ce DBG0); + free_data_fclose: + free(data); + fclose(f); +#ifdef DEBUG_HASH + sort_return: +#endif + /* Do we need read/write locks to sort the rr_l list? + As long as at most one thread is sorting, it is OK for the other threads + to read the cache, providing they do not add or delete anything. + */ + lock_cache_r(); + if(!insert_sort) { + sort_rrl(); + insert_sort=1; + } + unlock_cache_r(); + return; + + free_cent_data_fclose_exit: + free_cent(&ce DBG0); + free_data_fclose_exit: + free(data); + fclose_exit: + fclose(f); + log_error("Out of memory in reading cache file. Exiting."); + pdnsd_exit(); +} + +/* write an rr to the file f */ +static int write_rrset(int tp, rr_set_t *rrs, FILE *f) +{ + rr_bucket_t *rr; + rr_fset_t sh; + rr_fbucket_t rf; + unsigned num_rr; + + sh.tp=tp; + + num_rr=0; + for(rr=rrs->rrs; rr && num_rr<255; rr=rr->next) ++num_rr; + sh.num_rr=num_rr; + sh.flags=rrs->flags; + sh.ttl=rrs->ttl; + sh.ts=rrs->ts; + + if (fwrite(&sh,sizeof(sh),1,f)!=1) { + log_error("Error while writing rr header to disk cache: %s", strerror(errno)); + return 0; + } + + rr=rrs->rrs; + for(; num_rr; --num_rr) { + rf.rdlen=rr->rdlen; + if (fwrite(&rf,sizeof(rf),1,f)!=1 || (rf.rdlen && fwrite((rr->data),rf.rdlen,1,f)!=1)) { + log_error("Error while writing rr data to disk cache: %s", strerror(errno)); + return 0; + } + rr=rr->next; + } + + return 1; +} + + +/* + * Write cache to disk on termination. The hash table is lost and needs to be regenerated + * on reload. + * + * The locks are not very fine grained here, but I don't think this needs fixing as this routine + * is only called on exit. + * + */ +void write_disk_cache() +{ + int j, jlim; + dns_cent_t *le; + unsigned long en=0; + dns_hash_pos_t pos; + FILE *f; + unsigned long num_rrs_errs=0; +# define MAX_NUM_RRS_ERRS 10 + + char path[strlen(global.cache_dir)+sizeof("/pdnsd.cache")]; + + stpcpy(stpcpy(path,global.cache_dir),"/pdnsd.cache"); + + DEBUG_MSG("Writing cache to %s\n",path); + + if (!softlock_cache_rw()) { + goto lock_failed; + } + /* purge cache down to allowed size*/ + purge_cache((long)global.perm_cache*1024, 0); + if (!softunlock_cache_rw()) { + goto lock_failed; + } + + if (!softlock_cache_r()) { + goto lock_failed; + } + + if (!(f=fopen(path,"w"))) { + log_warn("Could not open disk cache file %s: %s",path,strerror(errno)); + goto softunlock_return; + } + + /* Write the cache version identifier */ + if (fwrite(cachverid,sizeof(cachverid),1,f)!=1) { + log_error("Error while writing cache version identifier to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + for (le=fetch_first(&pos); le; le=fetch_next(&pos)) { + /* count the rr's */ + if(le->flags&DF_NEGATIVE) { + if(!(le->flags&DF_LOCAL)) + ++en; + } + else { + jlim= RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) { + ++en; + break; + } + } + } + } + if (fwrite(&en,sizeof(en),1,f)!=1) { + log_error("Error while writing entry count to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + for (le=fetch_first(&pos); le; le=fetch_next(&pos)) { + /* now, write the rr's */ + if(le->flags&DF_NEGATIVE) { + if(!(le->flags&DF_LOCAL)) + goto write_rrs; + } + else { + jlim= RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) { + goto write_rrs; + } + } + } + continue; + write_rrs: + { + dns_file_t df; + int num_rrs; + const unsigned short *iterlist; + df.qlen=rhnlen(le->qname)-1; /* Don't include the null byte at the end */ + df.num_rrs=0; + df.flags=le->flags; + df.c_ns=le->c_ns; df.c_soa=le->c_soa; + num_rrs=0; + jlim=RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) + ++df.num_rrs; + } + } + if(num_rrs!=le->num_rrs && ++num_rrs_errs<=MAX_NUM_RRS_ERRS) { + unsigned char buf[DNSNAMEBUFSIZE]; + log_warn("Counted %d rr record types for %s but cached counter=%d", + num_rrs,rhn2str(le->qname,buf,sizeof(buf)),le->num_rrs); + } + if (fwrite(&df,sizeof(df),1,f)!=1) { + log_error("Error while writing cache entry header to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + if(le->flags&DF_NEGATIVE) { + dom_fttlts_t fttlts= {le->neg.ttl,le->neg.ts}; + if (fwrite(&fttlts,sizeof(fttlts),1,f)!=1) { + log_error("Error while writing cache TTL and timestamp to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + } + if (df.qlen && fwrite(le->qname,df.qlen,1,f)!=1) { + log_error("Error while writing domain name to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + jlim= NRRITERLIST(le); + iterlist= RRITERLIST(le); + for (j=0; jflags&CF_LOCAL)) { + if(!write_rrset(tp,rrset,f)) + goto fclose_unlock; + } + } + } + } + if(fclose(f)) { + log_error("Could not close cache file %s after writing cache: %s", path,strerror(errno)); + } + softunlock_cache_r(); + DEBUG_MSG("Finished writing cache to disk.\n"); + return; + + fclose_unlock: + fclose(f); + softunlock_return: + softunlock_cache_r(); + return; + + lock_failed: + crash_msg("Lock failed; could not write disk cache."); +} + +/* + * Conflict Resolution. + * The first function is the actual checker; the latter two are wrappers for the respective + * function for convenience only. + * + * We check for conflicts by checking the new data rrset by rrset against the cent. + * This is not bad when considering that new records are hopefully consistent; if they are not, + * we might end up deleteing too much of the old data, which is probably added back through the + * new query, though. + * Having checked additions rrset by rrset, we are at least sure that the resulting record is OK. + * cr_check_add returns 1 if the addition is OK, 0 otherwise. + * This is for records that are already in the cache! + * + * idx is the internally used RR-set index, not the RR type! + */ +static int cr_check_add(dns_cent_t *cent, int idx, time_t ttl, time_t ts, unsigned flags) +{ + time_t nttl; + const struct rr_infos *rri; + + if (flags & CF_NEGATIVE) + return 1; /* no constraints here. */ + + nttl = 0; + rri = &rr_info[idx]; + + if (!(flags & CF_LOCAL)) { + int i, ilim, ncf; + + if(cent->flags & DF_LOCAL) + return 0; /* Local has precedence. */ + + ncf = 0; ilim = RRARR_LEN(cent); + for (i = 0; i < ilim; ++i) { + rr_set_t *rrs= RRARR_INDEX(cent,i); + /* Should be symmetric; check both ways anyway. */ + if (rrs && !(rrs->flags & CF_NEGATIVE) && + ((rri->class & rr_info[i].excludes) || + (rri->excludes & rr_info[i].class))) + { + time_t rttl; + if (rrs->flags & CF_LOCAL) + return 0; /* old was authoritative. */ + ++ncf; + rttl = rrs->ttl + rrs->ts - time(NULL); + if(rttl > 0) nttl += rttl; + } + } + if (ncf == 0) /* no conflicts */ + return 1; + /* Medium ttl of conflicting records */ + nttl /= ncf; + } + if ((flags & CF_LOCAL) || ttl > nttl) { + int i, ilim= RRARR_LEN(cent); + + /* Remove the old records, so that the new one can be added. */ + for (i = 0; i < ilim; ++i) { + rr_set_t *rrs= RRARR_INDEX(cent,i); + /* Should be symmetric; check both ways anyway. */ + if (rrs && !(rrs->flags & CF_NEGATIVE) && + ((rri->class & rr_info[i].excludes) || + (rri->excludes & rr_info[i].class))) { + del_cent_rrset_by_index(cent, i DBG0); + } + } + return 1; + } + /* old records precede */ + return 0; +} + + +inline static void adjust_ttl(rr_set_t *rrset) +{ + if (rrset->flags&CF_NOCACHE) { + rrset->flags &= ~CF_NOCACHE; + rrset->ttl=0; + } + else { + time_t min_ttl= global.min_ttl, neg_ttl=global.neg_ttl; + if((rrset->flags&CF_NEGATIVE) && neg_ttlttlttl=min_ttl; + else { + time_t max_ttl= global.max_ttl; + if(rrset->ttl>max_ttl) + rrset->ttl=max_ttl; + } + } +} + + +/* Only use for negatively cached domains, thus only + if the DF_NEGATIVE bit is set! */ +inline static void adjust_dom_ttl(dns_cent_t *cent) +{ + if (cent->flags&DF_NOCACHE) { + cent->flags &= ~DF_NOCACHE; + cent->neg.ttl=0; + } + else { + time_t min_ttl= global.min_ttl, neg_ttl=global.neg_ttl; + if(/* (cent->flags&DF_NEGATIVE) && */ neg_ttlneg.ttlneg.ttl=min_ttl; + else { + time_t max_ttl= global.max_ttl; + if(cent->neg.ttl>max_ttl) + cent->neg.ttl=max_ttl; + } + } +} + +/* + * Add a ready built dns_cent_t to the hashes, purge if necessary to not exceed cache size + * limits, and add the entries to the hashes. + * As memory is already reserved for the rrs, we only need to wrap up the dns_cent_t and + * alloc memory for it. + * New entries are appended, so we easiliy know the oldest for purging. For fast acces, + * we use hashes instead of ordered storage. + * + * This does not free the argument, and it uses a copy of it, so the caller must do free_cent() + * on it. + * + * The new entries rr sets replace the old ones, i.e. old rr sets with the same key are deleted + * before the new ones are added. + */ +void add_cache(dns_cent_t *cent) +{ + dns_cent_t *ce; + dns_hash_loc_t loc; + int i,ilim; + + lock_cache_rw(); + retry: + if (!(ce=dns_lookup(cent->qname,&loc))) { + /* if the new entry doesn't contain any information, + don't try to add it to the cache because purge_cache() will not + be able to get rid of it. + */ + if(cent->num_rrs==0 && !(cent->flags&DF_NEGATIVE)) + goto purge_cache_return; + + if(!(ce=copy_cent(cent DBG0))) + goto warn_unlock_cache_return; + + if(!(ce->flags&DF_NEGATIVE)) { + ilim= RRARR_LEN(ce); + /* Add the rrs to the rr list */ + for (i=0; iflags&DF_NEGATIVE) { + /* the new entry is negative. So, we need to delete the whole cent, + * and then generate a new one. */ + ilim= RRARR_LEN(ce); + for (i=0; iflags&CF_LOCAL) { + goto unlock_cache_return; /* Do not clobber local records */ + } + } + del_cache_ent(ce,&loc); + goto retry; + } + purge_cent(ce, 0,0); + /* We have a record; add the rrsets replacing old ones */ + cache_size-=ce->cs; + + ilim= RRARR_LEN(cent); + for (i=0; iflags&CF_LOCAL) && (cerrs->flags&CF_LOCAL)) || + ((centrrs->flags&CF_ADDITIONAL) && (!(cerrs->flags&CF_ADDITIONAL) || + (!(centrrs->flags&CF_ROOTSERV) && + (cerrs->flags&CF_ROOTSERV))) && + !timedout(cerrs))))) + { + rr_bucket_t *rr,*rtail; + + del_cent_rrset_by_index(ce,i DBG0); + + if (!cr_check_add(ce, i, centrrs->ttl, centrrs->ts, centrrs->flags)) + continue; /* the new record has been deleted as a conflict resolution measure. */ + + /* pre-initialize a rrset_t for the case we have a negative cached + * rrset, in which case no further rrs will be added. */ + if (!add_cent_rrset_by_index(ce, i, centrrs->ttl, centrrs->ts, centrrs->flags DBG0)) { + goto addsize_unlock_cache_return; + } + rtail=NULL; + for (rr=centrrs->rrs; rr; rr=rr->next) { + if (!add_cent_rr_int(ce,i,centrrs->ttl, centrrs->ts, centrrs->flags, + rr->rdlen, rr->data, &rtail DBG0)) + { + /* cleanup this entry */ + goto cleanup_cent_unlock_cache_return; + } + } + cerrs= RRARR_INDEX(ce,i); + adjust_ttl(cerrs); + if (!insert_rrl(cerrs,ce,i)) { + goto cleanup_cent_unlock_cache_return; + } + } + } + } + ce->flags |= (cent->flags&(DF_AUTH|DF_WILD)); + if(cent->c_ns!=cundef && (ce->c_ns==cundef || ce->c_nsc_ns)) + ce->c_ns=cent->c_ns; + if(cent->c_soa!=cundef && (ce->c_soa==cundef || ce->c_soac_soa)) + ce->c_soa=cent->c_soa; + } + + cache_size += ce->cs; + purge_cache_return: + purge_cache((long)global.perm_cache*1024+MCSZ, 1); + goto unlock_cache_return; + + cleanup_cent_unlock_cache_return: + del_cent_rrset_by_index(ce, i DBG0); + addsize_unlock_cache_return: + cache_size += ce->cs; + goto warn_unlock_cache_return; + + free_cent_unlock_cache_return: + free_cent(ce DBG0); + pdnsd_free(ce); + warn_unlock_cache_return: + log_warn("Out of cache memory."); + unlock_cache_return: + unlock_cache_rw(); +} + +/* + Convert A (and AAAA) records in a ready built cache entry to PTR records suitable for reverse resolving + of numeric addresses and add them to the cache. +*/ +int add_reverse_cache(dns_cent_t * cent) +{ + int tp=T_A; + rr_set_t *rrset= getrrset_A(cent); + + for(;;) { + if(rrset) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + dns_cent_t ce; + unsigned char buf[DNSNAMEBUFSIZE],rhn[DNSNAMEBUFSIZE]; + if(!a2ptrstr((pdnsd_ca *)(rr->data),tp,buf) || !str2rhn(buf,rhn)) + return 0; + if(!init_cent(&ce, rhn, 0, 0, cent->flags DBG0)) + return 0; + if(!add_cent_rr(&ce,T_PTR,rrset->ttl,rrset->ts,rrset->flags,rhnlen(cent->qname),cent->qname DBG0)) { + free_cent(&ce DBG0); + return 0; + } +#ifdef RRMUINDEX_NS + ce.rr.rrmu[RRMUINDEX_NS]=cent->rr.rrmu[RRMUINDEX_NS]; +#endif +#ifdef RRMUINDEX_SOA + ce.rr.rrmu[RRMUINDEX_SOA]=cent->rr.rrmu[RRMUINDEX_SOA]; +#endif + add_cache(&ce); +#ifdef RRMUINDEX_NS + ce.rr.rrmu[RRMUINDEX_NS]=NULL; +#endif +#ifdef RRMUINDEX_SOA + ce.rr.rrmu[RRMUINDEX_SOA]=NULL; +#endif + free_cent(&ce DBG0); + } + } +#if ALLOW_LOCAL_AAAA + if(tp==T_AAAA) + break; + tp=T_AAAA; + rrset= getrrset_AAAA(cent); +#else + break; +#endif + } + return 1; +} + + +/* + Delete a cent from the cache. Call with write locks applied. + Does not delete corresponding entry in hash table, call del_cache_ent() + or del_cache() for that. +*/ +void del_cent(dns_cent_t *cent) +{ + cache_size -= cent->cs; + + /* free the data referred by the cent and the cent itself */ + free_cent(cent DBG0); + free(cent); + + --ent_num; +} + +/* + * Delete a cent from the cache. Call with write locks applied. + */ +static void del_cache_ent(dns_cent_t *cent,dns_hash_loc_t *loc) +{ + dns_cent_t *data; + + /* Delete from the hash */ + if(loc) + data=del_dns_hash_ent(loc); + else + data=del_dns_hash(cent->qname); + if(!data) { + log_warn("Cache entry not found by del_dns_hash() in %s, line %d",__FILE__,__LINE__); + } + else if(data!=cent) { + log_warn("pointer returned by del_dns_hash() does not match cache entry in %s, line %d",__FILE__,__LINE__); + } + del_cent(cent); +} + +/* Delete a cached record. Performs locking. Call this from the outside, NOT del_cache_ent */ +void del_cache(const unsigned char *name) +{ + dns_cent_t *cent; + + lock_cache_rw(); + if ((cent=del_dns_hash(name))) { + del_cent(cent); + } + unlock_cache_rw(); +} + + +/* Invalidate a record by resetting the fetch time to 0. This means that it will be refreshed + * if possible (and will only be served when purge_cache=off;) */ +void invalidate_record(const unsigned char *name) +{ + dns_cent_t *ce; + int i, ilim; + + lock_cache_rw(); + if ((ce=dns_lookup(name,NULL))) { + if(!(ce->flags&DF_NEGATIVE)) { + ilim= RRARR_LEN(ce); + for (i=0; its=0; + rrs->flags &= ~CF_AUTH; + } + } + } + else { + /* set the cent time to 0 (for the case that this was negative) */ + ce->neg.ts=0; + } + ce->flags &= ~DF_AUTH; + } + unlock_cache_rw(); +} + + +/* + Set flags of the cache entry with the specified name. + Don't use this to set the DF_NEGATIVE flag, or you will + risk leaving the cache in an inconsistent state. + Returns 0 if the cache entry cannot be found, otherwise 1. + */ +int set_cent_flags(const unsigned char *name, unsigned flags) +{ + dns_cent_t *ret; + lock_cache_rw(); + ret=dns_lookup(name,NULL); + if (ret) { + ret->flags |= flags; + } + unlock_cache_rw(); + return ret!=NULL; +} + +unsigned char *getlocalowner(unsigned char *name,int tp) +{ + unsigned char *ret=NULL; + dns_cent_t *ce; + unsigned lb; + + lock_cache_r(); + if((lb = *name)) { + while(name += lb+1, lb = *name) { + if((ce=dns_lookup(name,NULL))) { + if(!(ce->flags&DF_LOCAL)) + break; + if(have_rr(ce,tp)) { + ret=name; + break; + } + } + } + } + unlock_cache_r(); + + return ret; +} + + +/* Lookup an entry in the cache using name (in length byte - string notation). + * For thread safety, a copy must be returned, so delete it after use, by first doing + * free_cent to remove the rrs and then by freeing the returned pointer. + * If wild is nonzero, and name can't be found in the cache, lookup_cache() + * will search up the name hierarchy for a record with the DF_NEGATIVE or DF_WILD flag set. + */ +dns_cent_t *lookup_cache(const unsigned char *name, int *wild) +{ + int purge=0; + dns_cent_t *ret; + + /* First try with only read access to the cache. */ + lock_cache_r(); + ret=dns_lookup(name,NULL); + if(wild) { + *wild=0; + if(!ret) { + const unsigned char *nm=name; + unsigned lb=*nm; + if(lb) { + while(nm += lb+1, lb = *nm) { + if ((ret=dns_lookup(nm,NULL))) { + if(ret->flags&DF_NEGATIVE) + /* use this entry */ + *wild=w_neg; + else if(ret->flags&DF_WILD) { + unsigned char buf[DNSNAMEBUFSIZE]; + buf[0]=1; buf[1]='*'; + /* When we get here, at least one element of name + has been removed, so assuming name is not longer + than DNSNAMEBUFSIZE bytes, the remainder is guaranteed to + fit into DNSNAMEBUFSIZE-2 bytes */ + rhncpy(&buf[2],nm); + ret=dns_lookup(buf,NULL); + if(ret) + *wild=w_wild; + } + else if(ret->flags&DF_LOCAL) + *wild=w_locnerr; + else + ret=NULL; + break; + } + } + } + } + } + if (ret) { + if(!(purge=purge_cent(ret, 1,1))) /* test only, don't remove anything yet! */ + ret=copy_cent(ret DBG1); + } + unlock_cache_r(); + + if(purge) { + /* we need exclusive read and write access before we delete anything. */ + lock_cache_rw(); + ret=dns_lookup(name,NULL); + if(wild) { + *wild=0; + if(!ret) { + const unsigned char *nm=name; + unsigned lb=*nm; + if(lb) { + while(nm += lb+1, lb = *nm) { + if ((ret=dns_lookup(nm,NULL))) { + if(ret->flags&DF_NEGATIVE) + /* use this entry */ + *wild=w_neg; + else if(ret->flags&DF_WILD) { + unsigned char buf[DNSNAMEBUFSIZE]; + buf[0]=1; buf[1]='*'; + rhncpy(&buf[2],nm); + ret=dns_lookup(buf,NULL); + if(ret) + *wild=w_wild; + } + else if(ret->flags&DF_LOCAL) + *wild=w_locnerr; + else + ret=NULL; + break; + } + } + } + } + } + if (ret) { + if(purge_cent(ret, 1,0)<0) + ret=NULL; + else + ret=copy_cent(ret DBG1); + } + unlock_cache_rw(); + } + + return ret; +} + +/* lookup_cache_local_rrset() check if there is locally defined RR set of a specific RR type + for name, and if so, returns a copy of the RR set. After use, the copy should be cleaned + up using del_rrset(). + This is potentially much more efficient than using lookup_cache(), if the name is likely + to have a cache entry, but unlikely to have locally defined RR sets. +*/ +rr_set_t *lookup_cache_local_rrset(const unsigned char *name, int type) +{ + rr_set_t *ret=NULL; + dns_cent_t *cent; + + lock_cache_r(); + cent= dns_lookup(name,NULL); + if(cent) { + rr_set_t *rrset=getrrset(cent,type); + if(rrset && (rrset->flags&CF_LOCAL)) { + ret= copy_rrset(rrset); + } + } + unlock_cache_r(); + + return ret; +} + + +#if 0 +/* Add an rr to an existing cache entry or create a new entry if necessary. + * The rr is treated with the precedence of an additional or off-topic record, ie. regularly retrieved + * have precedence. + * You cannot add a negative additional record. Makes no sense anyway. */ +int add_cache_rr_add(const unsigned char *name, int tp, time_t ttl, time_t ts, unsigned flags, unsigned dlen, void *data, unsigned long serial) +{ + dns_hash_loc_t loc; + dns_cent_t *ret; + rr_set_t *rrs; + int rv=0; + + lock_cache_rw(); + if (!(ret=dns_lookup(name,&loc))) { + if (!(ret=cache_malloc(sizeof(dns_cent_t)))) + goto unlock_return; + if(!init_cent(ret, name, 0, 0, 0 DBG0)) { + pdnsd_free(ret); + goto unlock_return; + } + if(!add_dns_hash(ret,&loc)) { + free_cent(ret DBG0); + pdnsd_free(ret); + goto unlock_return; + } + ++ent_num; + } + else { + /* purge the record. */ + purge_cent(ret,0,0); + cache_size-=ret->cs; + } + rrs=getrrset(ret,tp); + if (rrs && + ((rrs->flags&CF_NEGATIVE && !(rrs->flags&CF_LOCAL)) || + (rrs->flags&CF_NOPURGE && timedout(rrs)) || + (rrs->flags&CF_ADDITIONAL && rrs->serial!=serial) || + (rrs->serial==serial && rrs->ttl!=(ttlglobal.max_ttl?global.max_ttl:ttl))))) { + del_cent_rrset_by_type(ret,tp DBG0); + rrs=NULL; + } + if (rrs==NULL || rrs->serial==serial) { + if (cr_check_add(ret,rrlkuptab[tp-T_MIN],ttl,ts,flags)) { + if (add_cent_rr(ret,tp,ttl,ts,flags,dlen,data,serial DBG0)) { + rr_set_t *rrsnew; + if (!rrs && (rrsnew=getrrset(ret,tp)) && !insert_rrl(rrsnew,ret,rrlkuptab[tp-T_MIN])) { + del_cent_rrset_by_type(ret,tp DBG0); + } + else { + cache_size+=ret->cs; + purge_cent(ret,1,0); + rv=1; + goto unlock_return; + } + } + } + } else { + rv=1; + } + cache_size+=ret->cs; + + unlock_return: + unlock_cache_rw(); + return rv; +} +#endif + +/* Report the cache status to the file descriptor f, for the status fifo (see status.c) */ +int report_cache_stat(int f) +{ + /* Cache size and entry counters are volatile (and even the entries + in the global struct can change), so make copies to get consistent data. + Even better would be to use locks, but that could be rather costly. */ + long csz= cache_size, en= ent_num; + long pc= global.perm_cache; + long mc= pc*1024+MCSZ; + + fsprintf_or_return(f,"\nCache status:\n=============\n"); + fsprintf_or_return(f,"%ld kB maximum disk cache size.\n",pc); + fsprintf_or_return(f,"%ld of %ld bytes (%.3g%%) memory cache used in %ld entries" + " (avg %.5g bytes/entry).\n", + csz, mc, (((double)csz)/mc)*100, en, + ((double)csz)/en); + return 0; +} + + +#define timestamp2str(ts,now,buf) \ +{ \ + struct tm tstm; \ + if(!((ts) && localtime_r(&(ts), &tstm) && \ + strftime(buf, sizeof(buf), \ + ((ts)<=(now) && (now)-(ts)<365*24*60*60/2)?" %m/%d %T":"%Y/%m/%d %T", \ + &tstm)>0)) \ + strcpy(buf," "); \ +} + +/* Dump contents of a cache entry to file descriptor fd. + Returns 1 on success, -1 if there is an IO error. +*/ +static int dump_cent(int fd, dns_cent_t *cent) +{ + time_t now; + char tstr[sizeof "2000/12/31 23:59:59"],dbuf[1024]; + + fsprintf_or_return(fd,"%s\n",rhn2str(cent->qname,ucharp dbuf,sizeof(dbuf))); + now=time(NULL); + + if(cent->flags&DF_NEGATIVE) { + timestamp2str(cent->neg.ts,now,tstr); + fsprintf_or_return(fd,"%s (domain negated)\n",tstr); + } + else { + int i, n= NRRITERLIST(cent); + const unsigned short *iterlist= RRITERLIST(cent); + for(i=0; its,now,tstr); + if(rrset->flags&CF_NEGATIVE) { + fsprintf_or_return(fd,"%s %-7s (negated)\n",tstr,rrnames[tp-T_MIN]); + } + else { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + switch (tp) { + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + rhn2str((unsigned char *)(rr->data),ucharp dbuf,sizeof(dbuf)); + break; +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + { + unsigned char *p=(unsigned char *)(rr->data); + int n; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + rhn2str(skiprhn(p),ucharp dbuf+n,sizeof(dbuf)-n); + } + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + { + unsigned char *p=(unsigned char *)(rr->data); + unsigned pref; + int n; + GETINT16(pref,p); + n=sprintf(dbuf,"%u ",pref); + if(n<0) goto hex_dump; + rhn2str(p,ucharp dbuf+n,sizeof(dbuf)-n); + } + break; + case T_SOA: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + int n,rem; + uint32_t serial,refresh,retry,expire,minimum; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + p=skiprhn(p); + rhn2str(p,ucharp q,rem); + n=strlen(q); + q[n++] = ' '; + if(n>=rem) + goto hex_dump; + q += n; + rem -= n; + p=skiprhn(p); + GETINT32(serial,p); + GETINT32(refresh,p); + GETINT32(retry,p); + GETINT32(expire,p); + GETINT32(minimum,p); + n=snprintf(q,rem,"%lu %lu %lu %lu %lu", + (unsigned long)serial,(unsigned long)refresh, + (unsigned long)retry,(unsigned long)expire, + (unsigned long)minimum); + if(n<0 || n>=rem) + goto hex_dump; + } + break; +#if IS_CACHED_HINFO || IS_CACHED_TXT || IS_CACHED_SPF +#if IS_CACHED_HINFO + case T_HINFO: +#endif +#if IS_CACHED_TXT + case T_TXT: +#endif +#if IS_CACHED_SPF + case T_SPF: +#endif + { + /* TXT records are not necessarily validated + before they are stored in the cache, so + we need to be careful. */ + unsigned char *p=(unsigned char *)(rr->data); + char *q=dbuf; + int j=0,n,rem=sizeof(dbuf); + while(jrdlen) { + unsigned lb; + if(rem<3) + goto hex_dump; + if(j) { + *q++ = ' '; + --rem; + } + *q++ = '"'; + --rem; + lb=*p++; + if((j += lb+1)>rr->rdlen) + goto hex_dump; + n=escapestr(charp p,lb,q,rem); + if(n<0 || n+1>=rem) + goto hex_dump; + q += n; + *q++ = '"'; + rem -= n+1; + p += lb; + } + *q=0; + } + break; +#endif +#if IS_CACHED_PX + case T_PX: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + unsigned pref; + int n,rem; + GETINT16(pref,p); + n=sprintf(dbuf,"%u ",pref); + if(n<0) goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + rhn2str(p,ucharp q,rem); + n=strlen(q); + q[n++] = ' '; + if(n>=rem) + goto hex_dump; + rhn2str(skiprhn(p),ucharp q+n,rem-n); + } + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + { + unsigned char *p=(unsigned char *)(rr->data); + unsigned priority,weight,port; + int n; + GETINT16(priority,p); + GETINT16(weight,p); + GETINT16(port,p); + n=sprintf(dbuf,"%u %u %u ",priority,weight,port); + if(n<0) goto hex_dump; + rhn2str(p,ucharp dbuf+n,sizeof(dbuf)-n); + } + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + { + unsigned char *p=(unsigned char *)(rr->data); + int n,rlen; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + rlen=rhnlen(p); + hexdump(p+rlen,rr->rdlen-rlen,dbuf+n,sizeof(dbuf)-n); + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + unsigned order,pref; + int n,rem,j; + GETINT16(order,p); + GETINT16(pref,p); + n=sprintf(dbuf,"%u %u ",order,pref); + if(n<0) goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + for (j=0;j<3;++j) { + unsigned lb; + if(rem<2) + goto hex_dump; + *q++ = '"'; + --rem; + lb=*p++; + n=escapestr(charp p,lb,q,rem); + if(n<0 || n+2>=rem) + goto hex_dump; + q += n; + *q++ = '"'; + *q++ = ' '; + rem -= n+2; + p += lb; + } + rhn2str(p,ucharp q,rem); + } + break; +#endif +#if IS_CACHED_LOC + case T_LOC: + /* Binary data length has not necessarily been validated */ + if(rr->rdlen!=16) + goto hex_dump; + if(!loc2str(rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#endif + case T_A: + if (!inet_ntop(AF_INET,rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#if IS_CACHED_AAAA && defined(AF_INET6) + case T_AAAA: + if (!inet_ntop(AF_INET6,rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#endif + default: + hex_dump: + hexdump(rr->data,rr->rdlen,dbuf,sizeof(dbuf)); + } + fsprintf_or_return(fd,"%s %-7s %s\n",tstr,rrnames[tp-T_MIN],dbuf); + } + } + } + } + } + fsprintf_or_return(fd,"\n"); + return 1; +} + +/* Dump cache contents to file descriptor fd. + If name is not null, restricts information to that name, + otherwise dumps information about all names found in the cache. + Returns 1 on success, 0 if the name is not found, -1 is there is an IO error. + Mainly for debugging purposes. +*/ +int dump_cache(int fd, const unsigned char *name, int exact) +{ + int rv=0; + lock_cache_r(); + if(name && exact) { + dns_cent_t *cent=dns_lookup(name,NULL); + if(cent) + rv=dump_cent(fd,cent); + } + else { + dns_cent_t *cent; + dns_hash_pos_t pos; + for (cent=fetch_first(&pos); cent; cent=fetch_next(&pos)) { + unsigned int nrem; + if(!name || (domain_match(name,cent->qname,&nrem,NULL),nrem==0)) + if((rv=dump_cent(fd,cent))<0) + break; + } + } + unlock_cache_r(); + return rv; +} + + +#if DEBUG>0 + +/* Added by Paul Rombouts: This is only used in debug messages. */ +const char cflgnames[NCFLAGS*3]={'N','E','G','L','O','C','A','U','T','N','O','C','A','D','D','N','O','P','R','T','S'}; +const char dflgnames[NDFLAGS*3]={'N','E','G','L','O','C','A','U','T','N','O','C','W','L','D'}; + +char *flags2str(unsigned flags,char *buf,int nflags,const char *flgnames) +{ + char *p=buf; + int i,nflgchars=3*nflags; + for(i=0;ibuf) *p++='|'; + p=mempcpy(p,&flgnames[i],3); + } + flags >>= 1; + } + if(p==buf) + *p++='0'; + *p=0; + return buf; +} +#endif diff --git a/app/src/main/jni/pdnsd/src/cache.h b/app/src/main/jni/pdnsd/src/cache.h new file mode 100644 index 0000000..5056dec --- /dev/null +++ b/app/src/main/jni/pdnsd/src/cache.h @@ -0,0 +1,306 @@ +/* cache.h - Definitions for the dns cache + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include +#include "ipvers.h" +#include +#include "list.h" +#include "dns.h" +#include "conff.h" + +struct rr_lent_s; + +/* + * These values are converted to host byte order. the data is _not_. + */ +typedef struct rr_b_s { + struct rr_b_s *next; /* this is the next pointer in the dns_cent_t list. */ + unsigned rdlen; +#if ALLOW_LOCAL_AAAA || defined(ENABLE_IPV6) + struct in6_addr data[0]; /* dummy for alignment */ +#else + struct in_addr data[0]; +#endif +} rr_bucket_t; + +typedef struct { + struct rr_lent_s *lent; /* this points to the list entry */ + time_t ttl; + time_t ts; + unsigned short flags; + rr_bucket_t *rrs; +} rr_set_t; + + +typedef struct { + unsigned char *qname; /* Name of the domain in length byte - string notation. */ + size_t cs; /* Size of the cache entry, including RR sets. */ + unsigned short num_rrs; /* The number of RR sets. When this decreases to 0, the cent is deleted. */ + unsigned short flags; /* Flags for the whole domain. */ + union { + struct { /* Fields used only for negatively cached domains. */ + struct rr_lent_s *lent; /* list entry for the whole cent. */ + time_t ttl; /* TTL for negative caching. */ + time_t ts; /* Timestamp. */ + } neg; + struct { /* Fields used only for domains that actually exist. */ + rr_set_t *(rrmu[NRRMU]); /* The most used records. + Use the the value obtained from rrlkuptab[] as index. */ + rr_set_t **rrext; /* Pointer (may be NULL) to an array of size NNRREXT storing the + less frequently used records. */ + } rr; + }; + unsigned char c_ns,c_soa; /* Number of trailing name elements in qname to use to find NS or SOA + records to add to the authority section of a response. */ +} dns_cent_t; + +/* This value is used to represent an undefined c_ns or c_soa field. */ +#define cundef 0xff + +/* + * the flag values for RR sets in the cache + */ +#define CF_NEGATIVE 1 /* this one is for per-RRset negative caching*/ +#define CF_LOCAL 2 /* Local zone entry */ +#define CF_AUTH 4 /* authoritative record */ +#define CF_NOCACHE 8 /* Only hold for the cache latency time period, then purge. + * Not really written to cache, but used by add_cache. */ +#define CF_ADDITIONAL 16 /* This was fetched as an additional or "off-topic" record. */ +#define CF_NOPURGE 32 /* Do not purge this record */ +#define CF_ROOTSERV 64 /* This record was directly obtained from a root server */ + +#define CFF_NOINHERIT (CF_LOCAL|CF_AUTH|CF_ADDITIONAL|CF_ROOTSERV) /* not to be inherited on requery */ + +/* + * the flag values for whole domains in the cache + */ +#define DF_NEGATIVE 1 /* this one is for whole-domain negative caching (created on NXDOMAIN)*/ +#define DF_LOCAL 2 /* local record (in conj. with DF_NEGATIVE) */ +#define DF_AUTH 4 /* authoritative record */ +#define DF_NOCACHE 8 /* Only hold for the cache latency time period, then purge. + * Only used for negatively cached domains. + * Not really written to cache, but used by add_cache. */ +#define DF_WILD 16 /* subdomains of this domain have wildcard records */ + +/* #define DFF_NOINHERIT (DF_NEGATIVE) */ /* not to be inherited on requery */ + +enum {w_wild=1, w_neg, w_locnerr}; /* Used to distinguish different types of wildcard records. */ + +#if DEBUG>0 +#define NCFLAGS 7 +#define NDFLAGS 5 +#define CFLAGSTRLEN (NCFLAGS*4) +#define DFLAGSTRLEN (NDFLAGS*4) +extern const char cflgnames[]; +extern const char dflgnames[]; +char *flags2str(unsigned flags,char *buf,int nflags,const char *flgnames); +#define cflags2str(flags,buf) flags2str(flags,buf,NCFLAGS,cflgnames) +#define dflags2str(flags,buf) flags2str(flags,buf,NDFLAGS,dflgnames) +#endif + +/* + * This is the time in secs any record remains at least in the cache before it is purged. + * (exception is that the cache is full) + */ +#define CACHE_LAT 120 +#define CLAT_ADJ(ttl) ((ttl)ts+CLAT_ADJ((rrset)->ttl)neg.ts+CLAT_ADJ((cent)->neg.ttl)purge_cache) + fl|=CF_NOPURGE; + if (server->nocache) + fl|=CF_NOCACHE; + if (server->rootserver) + fl|=CF_ROOTSERV; + return fl; +} + +int init_cent(dns_cent_t *cent, const unsigned char *qname, time_t ttl, time_t ts, unsigned flags DBGPARAM); +int add_cent_rrset_by_type(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags DBGPARAM); +int add_cent_rr(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags,unsigned dlen, void *data DBGPARAM); +int del_rrset(rr_set_t *rrs DBGPARAM); +void free_cent(dns_cent_t *cent DBGPARAM); +void free_cent0(void *ptr); +void negate_cent(dns_cent_t *cent, time_t ttl, time_t ts); +void del_cent(dns_cent_t *cent); + +/* Because this is empty by now, it is defined as an empty macro to save overhead.*/ +/*void free_rr(rr_bucket_t cent);*/ +#define free_rr(x) + +dns_cent_t *copy_cent(dns_cent_t *cent DBGPARAM); + +#if 0 +unsigned long get_serial(void); +#endif + +/* Get pointer to rrset given cache entry and rr type value. */ +inline static rr_set_t *getrrset(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static rr_set_t *getrrset(dns_cent_t *cent, int type) +{ + if(!(cent->flags&DF_NEGATIVE)) { + int tpi= type - T_MIN; + + if(tpi>=0 && tpirr.rrmu[idx]; + else { + idx -= NRRMU; + if(idx < NRREXT) { + rr_set_t **rrext= cent->rr.rrext; + if(rrext) + return rrext[idx]; + } + } + } + } + + return NULL; +} + +/* This version of getrrset is slightly more efficient, + but also more dangerous, because it performs less checks. + It is safe to use if T_MIN <= type <= T_MAX and cent + is not negative. +*/ +inline static rr_set_t *getrrset_eff(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static rr_set_t *getrrset_eff(dns_cent_t *cent, int type) +{ + unsigned int idx = rrlkuptab[type-T_MIN]; + if(idx < NRRMU) + return cent->rr.rrmu[idx]; + else { + idx -= NRRMU; + if(idx < NRREXT) { + rr_set_t **rrext= cent->rr.rrext; + if(rrext) + return rrext[idx]; + } + } + + return NULL; +} + + +/* have_rr() tests whether a cache entry has at least one record of a given type. + Only use if T_MIN <= type <=T_MAX +*/ +inline static int have_rr(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static int have_rr(dns_cent_t *cent, int type) +{ + rr_set_t *rrset; + return !(cent->flags&DF_NEGATIVE) && (rrset=getrrset_eff(cent, type)) && rrset->rrs; +} + +/* Some quick and dirty and hopefully fast macros. */ +#define PDNSD_NOT_CACHED_TYPE(type) ((type)T_MAX || rrlkuptab[(type)-T_MIN]>=NRRTOT) + +/* This is useful for iterating over all the RR types in a cache entry in strict ascending order. */ +#define NRRITERLIST(cent) ((cent)->flags&DF_NEGATIVE?0:(cent)->rr.rrext?NRRTOT:NRRMU) +#define RRITERLIST(cent) ((cent)->flags&DF_NEGATIVE?NULL:(cent)->rr.rrext?rrcachiterlist:rrmuiterlist) + +/* The following macros use array indices as arguments, not RR type values! */ +#define GET_RRSMU(cent,i) (!((cent)->flags&DF_NEGATIVE)?(cent)->rr.rrmu[i]:NULL) +#define GET_RRSEXT(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrext?(cent)->rr.rrext[i]:NULL) +#define HAVE_RRMU(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrmu[i] && (cent)->rr.rrmu[i]->rrs) +#define HAVE_RREXT(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrext && (cent)->rr.rrext[i] && (cent)->rr.rrext[i]->rrs) + +#define RRARR_LEN(cent) ((cent)->flags&DF_NEGATIVE?0:(cent)->rr.rrext?NRRTOT:NRRMU) + +/* This allows us to index the RR-set arrays in a cache entry as if they formed one contiguous array. */ +#define RRARR_INDEX_TESTEXT(cent,i) ((cent)->flags&DF_NEGATIVE?NULL:(i)rr.rrmu[i]:(cent)->rr.rrext?(cent)->rr.rrext[(i)-NRRMU]:NULL) +/* This gets the address where the pointer to an RR-set is stored in a cache entry, + given the cache entry and an RR-set index. + Address may be NULL if no storage space for the type has been allocated. */ +#define RRARR_INDEX_PA_TESTEXT(cent,i) ((cent)->flags&DF_NEGATIVE?NULL:(i)rr.rrmu[i]:(cent)->rr.rrext?&(cent)->rr.rrext[(i)-NRRMU]:NULL) + +/* The following macros should only be used if 0 <= i < RRARR_LEN(cent) ! */ +#define RRARR_INDEX(cent,i) ((i)rr.rrmu[i]:(cent)->rr.rrext[(i)-NRRMU]) +#define RRARR_INDEX_PA(cent,i) ((i)rr.rrmu[i]:&(cent)->rr.rrext[(i)-NRRMU]) + +#endif diff --git a/app/src/main/jni/pdnsd/src/conf-keywords.h b/app/src/main/jni/pdnsd/src/conf-keywords.h new file mode 100644 index 0000000..2bcdacf --- /dev/null +++ b/app/src/main/jni/pdnsd/src/conf-keywords.h @@ -0,0 +1,238 @@ +/* conf-keywords.h - Tables used by parser of configuration file. + Based on information previously contained in conf-lex.y and conf-parse.y + + Copyright (C) 2004,2005,2006,2007,2008,2009,2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +enum { + ERROR, + + GLOBAL, + SERVER, + RR, + NEG, + SOURCE, + INCLUDE_F, + + PERM_CACHE, + CACHE_DIR, + SERVER_PORT, + SERVER_IP, + OUTGOING_IP, + SCHEME_FILE, + LINKDOWN_KLUGE, + MAX_TTL, + MIN_TTL, + RUN_AS, + STRICT_SETUID, + USE_NSS, + PARANOID, + IGNORE_CD, + STATUS_CTL, + DAEMON, + C_TCP_SERVER, + PID_FILE, + C_VERBOSITY, + C_QUERY_METHOD, + RUN_IPV4, + IPV4_6_PREFIX, + C_DEBUG, + C_CTL_PERMS, + C_PROC_LIMIT, + C_PROCQ_LIMIT, + TCP_QTIMEOUT, + C_PAR_QUERIES, + C_RAND_RECS, + NEG_TTL, + NEG_RRS_POL, + NEG_DOMAIN_POL, + QUERY_PORT_START, + QUERY_PORT_END, + UDP_BUFSIZE, + DELEGATION_ONLY, + + IP, + PORT, + SCHEME, + UPTEST, + TIMEOUT, + PING_TIMEOUT, + PING_IP, + UPTEST_CMD, + QUERY_TEST_NAME, + INTERVAL, + INTERFACE, + DEVICE, + PURGE_CACHE, + CACHING, + LEAN_QUERY, + EDNS_QUERY, + PRESET, + PROXY_ONLY, + ROOT_SERVER, + RANDOMIZE_SERVERS, + INCLUDE, + EXCLUDE, + POLICY, + REJECTLIST, + REJECTPOLICY, + REJECTRECURSIVELY, + LABEL, + + A, + PTR, + MX, + SOA, + CNAME, + TXT, + SPF, + NAME, + OWNER, + TTL, + TYPES, + FILET, + SERVE_ALIASES, + AUTHREC, + REVERSE +}; + + +/* Table for looking up section headers. Order alphabetically! */ +static const namevalue_t section_headers[]= { + {"global", GLOBAL}, + {"include", INCLUDE_F}, + {"neg", NEG}, + {"rr", RR}, + {"server", SERVER}, + {"source", SOURCE} +}; + +/* Table for looking up global options. Order alphabetically! */ +static const namevalue_t global_options[]= { + {"cache_dir", CACHE_DIR}, + {"ctl_perms", C_CTL_PERMS}, + {"daemon", DAEMON}, + {"debug", C_DEBUG}, + {"delegation_only", DELEGATION_ONLY}, + {"ignore_cd", IGNORE_CD}, + {"interface", SERVER_IP}, + {"ipv4_6_prefix", IPV4_6_PREFIX}, + {"linkdown_kluge", LINKDOWN_KLUGE}, + {"max_ttl", MAX_TTL}, + {"min_ttl", MIN_TTL}, + {"neg_domain_pol", NEG_DOMAIN_POL}, + {"neg_rrs_pol", NEG_RRS_POL}, + {"neg_ttl", NEG_TTL}, + {"outgoing_ip", OUTGOING_IP}, + {"outside_interface", OUTGOING_IP}, + {"par_queries", C_PAR_QUERIES}, + {"paranoid", PARANOID}, + {"perm_cache", PERM_CACHE}, + {"pid_file", PID_FILE}, + {"proc_limit", C_PROC_LIMIT}, + {"procq_limit", C_PROCQ_LIMIT}, + {"query_method", C_QUERY_METHOD}, + {"query_port_end", QUERY_PORT_END}, + {"query_port_start", QUERY_PORT_START}, + {"randomize_recs", C_RAND_RECS}, + {"run_as", RUN_AS}, + {"run_ipv4", RUN_IPV4}, + {"scheme_file", SCHEME_FILE}, + {"server_ip", SERVER_IP}, + {"server_port", SERVER_PORT}, + {"status_ctl", STATUS_CTL}, + {"strict_setuid", STRICT_SETUID}, + {"tcp_qtimeout", TCP_QTIMEOUT}, + {"tcp_server", C_TCP_SERVER}, + {"timeout", TIMEOUT}, + {"udpbufsize", UDP_BUFSIZE}, + {"use_nss", USE_NSS}, + {"verbosity", C_VERBOSITY} +}; + +/* Table for looking up server options. Order alphabetically! */ +static const namevalue_t server_options[]= { + {"caching", CACHING}, + {"device", DEVICE}, + {"edns_query", EDNS_QUERY}, + {"exclude", EXCLUDE}, + {"file", FILET}, + {"include", INCLUDE}, + {"interface", INTERFACE}, + {"interval", INTERVAL}, + {"ip", IP}, + {"label", LABEL}, + {"lean_query", LEAN_QUERY}, + {"ping_ip", PING_IP}, + {"ping_timeout", PING_TIMEOUT}, + {"policy", POLICY}, + {"port", PORT}, + {"preset", PRESET}, + {"proxy_only", PROXY_ONLY}, + {"purge_cache", PURGE_CACHE}, + {"query_test_name", QUERY_TEST_NAME}, + {"randomize_servers", RANDOMIZE_SERVERS}, + {"reject", REJECTLIST}, + {"reject_policy", REJECTPOLICY}, + {"reject_recursively", REJECTRECURSIVELY}, + {"root_server", ROOT_SERVER}, + {"scheme", SCHEME}, + {"timeout", TIMEOUT}, + {"uptest", UPTEST}, + {"uptest_cmd", UPTEST_CMD} +}; + +/* Table for looking up rr options. Order alphabetically! */ +static const namevalue_t rr_options[]= { + {"a", A}, + {"authrec", AUTHREC}, + {"cname", CNAME}, + {"mx", MX}, + {"name", NAME}, + {"ns", OWNER}, + {"owner", OWNER}, + {"ptr", PTR}, + {"reverse", REVERSE}, + {"soa", SOA}, + {"spf", SPF}, + {"ttl", TTL}, + {"txt", TXT} +}; + +/* Table for looking up source options. Order alphabetically! */ +static const namevalue_t source_options[]= { + {"authrec", AUTHREC}, + {"file", FILET}, + {"ns", OWNER}, + {"owner", OWNER}, + {"serve_aliases", SERVE_ALIASES}, + {"ttl", TTL} +}; + +/* Table for looking up include options. Order alphabetically! */ +static const namevalue_t include_options[]= { + {"file", FILET} +}; + +/* Table for looking up neg options. Order alphabetically! */ +static const namevalue_t neg_options[]= { + {"name", NAME}, + {"ttl", TTL}, + {"types", TYPES} +}; diff --git a/app/src/main/jni/pdnsd/src/conf-parser.c b/app/src/main/jni/pdnsd/src/conf-parser.c new file mode 100644 index 0000000..9cf9180 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/conf-parser.c @@ -0,0 +1,2118 @@ +/* conf-parser.c - Parser for pdnsd config files. + Based on the files conf-lex.l and conf-parse.y written by + Thomas Moestl. + This version was rewritten in C from scratch by Paul A. Rombouts + and doesn't require (f)lex or yacc/bison. + + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Paul A. Rombouts. + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include "ipvers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STRUCT_IFREQ) +#include +#endif +#include "conff.h" +#include "consts.h" +#include "cache.h" +#include "dns.h" +#include "helpers.h" +#include "rr_types.h" +#include "netdev.h" +#include "conf-keywords.h" +#include "conf-parser.h" + + +/* Check that include files are not nested deeper than MAXINCLUDEDEPTH, + as a precaution against infinite recursion. */ +#define MAXINCLUDEDEPTH 100 + +static char *report_error (const char *conftype, unsigned linenr, const char *msg) +{ + char *retval; + if(linenr) { + if(asprintf(&retval, "Error in %s (line %u): %s",conftype,linenr,msg)<0) + retval=NULL; + } + else { + if(asprintf(&retval, "Error in %s: %s",conftype,msg)<0) + retval=NULL; + } + + return retval; +} + +static char *report_errorf (const char *conftype, unsigned linenr, const char *frm,...) printfunc(3, 4); +static char *report_errorf (const char *conftype, unsigned linenr, const char *frm,...) +{ + char *msg,*retval; int mlen; + va_list va; + va_start(va,frm); + mlen=vasprintf(&msg,frm,va); + va_end(va); + if(mlen<0) return NULL; + retval=report_error(conftype,linenr,msg); + free(msg); + return retval; +} + +/* return pointer to next character in linebuffer after skipping blanks and comments */ +static char* getnextp(char **buf, size_t *n, FILE* in, char *p, unsigned *linenr, char **errstr) +{ + if(!p) goto nextline; + tryagain: + if(!*p) { + nextline: + do { + if(!in || getline(buf,n,in)<0) { + *errstr=NULL; + return NULL; + } + ++*linenr; + p=*buf; + } while(!*p); + } + if(isspace(*p)) { + ++p; goto tryagain; + } + if(*p=='#') { + skip_rest_of_line: + if(*linenr) + goto nextline; + else { + p=strchr(p,'\n'); + if(p) { + ++p; + goto tryagain; + } + else + goto nextline; + } + } + if(*p=='/') { + if(*(p+1)=='/') + goto skip_rest_of_line; + if(*(p+1)=='*') { + int lev=1; + p +=2; + for(;;) { + while(*p) { + if(*p=='/' && *(p+1)=='*') { + ++lev; + p +=2; + continue; + } + else if(*p=='*' && *(p+1)=='/') { + p +=2; + if(--lev==0) goto tryagain; + continue; + } + ++p; + } + if(!in || getline(buf,n,in)<0) { + *errstr="comment without closing */"; + return NULL; + } + ++*linenr; + p=*buf; + } + } + } + + return p; +} + +static char translescapedchar(char c) +{ + switch(c) { + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + } + return c; +} + +/* Scan a buffer for a string and copy the decoded (i.e. unescaped) version into + another buffer. + + A string either begins after and ends before a double-quote ("), + or simply consists of a sequence of "non-special" characters, + starting at the current position. + A back-slash (\) acts as an escape character, preventing any character + following it from terminating the string. Thus, for example, + back-slash double-quote (\") may be used to include double-quotes + in a string. + A number of escape sequences are interpreted as in C, e.g. + \t, \n, \r yield control-chars as in C. + + char **curp should point to the position in the buffer where + the scanning should begin. It will be updated to point + to the first character past the scanned string. + + char *outbuf is used to store the decoded string. + size_t outbufsz should be the size of outbuf. + + The return value is the length of the decoded string, unless an error occurs, + in which case -1 is returned and *errstr is assigned an error message. + The returned length may be larger than outbufsz, in which case the buffer + is filled with only the first outbufsz chars of the string. +*/ +static int scan_string(char **curp,char *outbuf, unsigned outbufsz, char **errstr) +{ + char *cur=*curp; + unsigned i=0; + + if(*cur=='"') { + /* Double-quoted string. */ + ++cur; /* Skip opening quote. */ + for(;; ++i,++cur) { + if(!*cur) goto noclosingquote; + if(*cur=='"') break; + if(*cur=='\\') { + if(!*++cur) goto nofollowingchar; + if(i=sizeof(buf)) \ + {CLEANUP_HANDLERS; goto string_too_long;} \ + } +# define REPORT_ERROR(msg) (*errstr=report_error(conftype,linenr,msg)) +# if !defined(CPP_C99_VARIADIC_MACROS) + /* GNU C Macro Varargs style. */ +# define REPORT_ERRORF(args...) (*errstr=report_errorf(conftype,linenr,args)) +#else + /* ANSI C99 style. */ +# define REPORT_ERRORF(...) (*errstr=report_errorf(conftype,linenr,__VA_ARGS__)) +# endif +# define PARSERROR {CLEANUP_HANDLERS; goto free_linebuf_return;} +# define OUTOFMEMERROR {CLEANUP_HANDLERS; goto out_of_memory;} +# define CLEANUP_GOTO(lab) {CLEANUP_HANDLERS; goto lab;} + + *errstr=NULL; + if(in) { + linebuf=malloc(buflen); + if(!linebuf) { + /* If malloc() just failed, allocating space for an error message is unlikely to succeed. */ + return 0; + } + if(global) + conftype="config file"; + else + conftype="include file"; + } + else + conftype="config string"; + + p=prestr; + while((p=getnextp(&linebuf,&buflen,in,p,&linenr,&getnextperr))) { + if(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + sechdr=lookup_keyword(ps,len,section_headers); + if(!sechdr) { + REPORT_ERRORF("invalid section header: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='{') goto expected_bropen; + ++p; + SKIP_BLANKS(p); + + switch(sechdr) { + case GLOBAL: + if(!global) { + REPORT_ERROR(in?"global section not allowed in include file": + "global section not allowed in eval string"); + PARSERROR; + } + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,global_options); + if(!option) { + REPORT_ERRORF("invalid option for global section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + pdnsd_a *ipaddrp; + + case PERM_CACHE: + if (isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_OFF) { + global->perm_cache=0; + } + else + goto bad_perm_cache_option; + } + else if(isdigit(*p)) { + global->perm_cache=strtol(p,&p,0); + } + else { + bad_perm_cache_option: + REPORT_ERROR("bad qualifier in perm_cache= option."); + PARSERROR; + } + break; + + case CACHE_DIR: + SCAN_STRING(p,strbuf,len); + STRNDUP(global->cache_dir,strbuf,len); + break; + + case SERVER_PORT: + SCAN_UNSIGNED_NUM(global->port,p,"server_port option") + break; + + case OUTGOING_IP: + ipaddrp= &global->out_a; + goto scan_ip_or_interface; + + case SERVER_IP: + ipaddrp= &global->a; + scan_ip_or_interface: + SCAN_STRING(p,strbuf,len); + { + const char *err; + if ((err=parse_ip(strbuf,ipaddrp))) { +#if defined(HAVE_STRUCT_IFREQ) && defined(IFNAMSIZ) && defined(SIOCGIFADDR) + if(!strcmp(err,"bad IP address") && lenipv4= ((struct sockaddr_in *)&req.ifr_addr)->sin_addr; +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 + ipaddrp->ipv6= ((struct sockaddr_in6 *)&req.ifr_addr)->sin6_addr; +# endif + close(fd); + } + else { + REPORT_ERRORF("Failed to get IP address of %s: %s",req.ifr_name,strerror(errno)); + if(fd!=-1) close(fd); + PARSERROR; + } + } + else +#endif + { + REPORT_ERRORF("%s for the %s= option.",err,option==SERVER_IP?"server_ip":"outgoing_ip"); + PARSERROR; + } + } + } + break; + + case SCHEME_FILE: + SCAN_STRING(p,strbuf,len); + STRNDUP(global->scheme_file, strbuf,len); + break; + + case LINKDOWN_KLUGE: + ASSIGN_ON_OFF(global->lndown_kluge,p,C_ON,"bad qualifier in linkdown_kluge= option."); + break; + + case MAX_TTL: + SCAN_TIMESECS(global->max_ttl,p,"max_ttl option"); + break; + + case MIN_TTL: + SCAN_TIMESECS(global->min_ttl,p,"min_ttl option"); + break; + + case RUN_AS: + SCAN_STRING(p,strbuf,len); + STRNCP(global->run_as, strbuf,len, "run_as"); + break; + + case STRICT_SETUID: + ASSIGN_ON_OFF(global->strict_suid, p,C_ON,"bad qualifier in strict_setuid= option."); + break; + + case USE_NSS: + ASSIGN_ON_OFF(global->use_nss, p,C_ON,"bad qualifier in use_nss= option."); + break; + + case PARANOID: + ASSIGN_ON_OFF(global->paranoid, p,C_ON,"bad qualifier in paranoid= option."); + break; + + case IGNORE_CD: { + int ignore_cd; + ASSIGN_ON_OFF(ignore_cd, p,C_ON,"bad qualifier in ignore_cd= option."); + fprintf(stderr, "Warning: ignore_cd option in configuration file is obsolete and currently has no effect.\n"); + } + break; + + case STATUS_CTL: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in status_pipe= option."); + if(!cmdline.stat_pipe) global->stat_pipe=(cnst==C_ON); + } + break; + + case DAEMON: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in daemon= option."); + if(!cmdline.daemon) global->daemon=(cnst==C_ON); + } + break; + + case C_TCP_SERVER: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in tcp_server= option."); + if(!cmdline.notcp) { + global->notcp=(cnst==C_OFF); +#ifdef NO_TCP_SERVER + if(!global->notcp) { + REPORT_ERROR("pdnsd was compiled without TCP server support. tcp_server=on is not allowed."); + PARSERROR; + } +#endif + } + } + break; + + case PID_FILE: + SCAN_STRING(p,strbuf,len); + if(!cmdline.pidfile) {STRNDUP(global->pidfile,strbuf,len);} + break; + + case C_VERBOSITY: { + int val; + SCAN_UNSIGNED_NUM(val,p,"verbosity option"); + if(!cmdline.verbosity) global->verbosity=val; + } + break; + + case C_QUERY_METHOD: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==TCP_ONLY || cnst==UDP_ONLY || cnst==TCP_UDP || cnst==UDP_TCP,"bad qualifier in query_method= option."); +#ifdef NO_TCP_QUERIES + if (cnst==TCP_ONLY) { + REPORT_ERROR("the tcp_only option is only available when pdnsd is compiled with TCP support."); + PARSERROR; + } + else +#endif +#ifdef NO_UDP_QUERIES + if (cnst==UDP_ONLY) { + REPORT_ERROR("the udp_only option is only available when pdnsd is compiled with UDP support."); + PARSERROR; + } + else +#endif +#if defined(NO_TCP_QUERIES) || defined(NO_UDP_QUERIES) + if (cnst==TCP_UDP) { + REPORT_ERROR("the tcp_udp option is only available when pdnsd is compiled with both TCP and UDP support."); + PARSERROR; + } + else if (cnst==UDP_TCP) { + REPORT_ERROR("the udp_tcp option is only available when pdnsd is compiled with both TCP and UDP support."); + PARSERROR; + } + else +#endif + if(!cmdline.query_method) global->query_method=cnst; + } + break; + + case RUN_IPV4: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"bad qualifier in run_ipv4= option."); +#ifndef ENABLE_IPV4 + if(cnst==C_ON) { + REPORT_ERROR("You can only set run_ipv4=on when pdnsd is compiled with IPv4 support."); + PARSERROR; + } +#endif +#ifndef ENABLE_IPV6 + if(cnst==C_OFF) { + REPORT_ERROR("You can only set run_ipv4=off when pdnsd is compiled with IPv6 support."); + PARSERROR; + } +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) { + run_ipv4=(cnst==C_ON); cmdlineipv=-1; + } + else if(cmdlineipv<0 && run_ipv4!=(cnst==C_ON)) { + REPORT_ERROR(cmdlineipv==-1? + "IPv4/IPv6 conflict: you are trying to set run_ipv4 to a value that conflicts with a previous run_ipv4 setting.": + "You must set the run_ipv4 option before specifying IP addresses."); + PARSERROR; + } +#endif + } + break; + + case IPV4_6_PREFIX: + SCAN_STRING(p,strbuf,len); +#ifdef ENABLE_IPV6 + if(!cmdline.prefix) { + if(inet_pton(AF_INET6,strbuf,&global->ipv4_6_prefix)<=0) { + REPORT_ERROR("ipv4_6_prefix: argument not a valid IPv6 address."); + PARSERROR; + } + } +#else + fprintf(stderr,"pdnsd was compiled without IPv6 support. ipv4_6_prefix option in config file will be ignored.\n"); +#endif + break; + + case C_DEBUG: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in debug= option."); + if(!cmdline.debug) { + global->debug=(cnst==C_ON); +#if !DEBUG + if(global->debug) + fprintf(stderr,"pdnsd was compiled without debugging support. debug=on has no effect.\n"); +#endif + } + } + break; + + case C_CTL_PERMS: + SCAN_UNSIGNED_NUM(global->ctl_perms, p,"ctl_perms option"); + break; + + case C_PROC_LIMIT: + SCAN_UNSIGNED_NUM(global->proc_limit, p,"proc_limit option"); + break; + + case C_PROCQ_LIMIT: + SCAN_UNSIGNED_NUM(global->procq_limit, p,"procq_limit option"); + break; + + case TCP_QTIMEOUT: + SCAN_TIMESECS(global->tcp_qtimeout, p,"tcp_qtimeout option"); + break; + + case TIMEOUT: + SCAN_TIMESECS(global->timeout, p,"global timeout option"); + break; + + case C_PAR_QUERIES: { + int val; + SCAN_UNSIGNED_NUM(val, p,"par_queries option"); + if(val<=0) { + REPORT_ERROR("bad value for par_queries."); + PARSERROR; + } else { + global->par_queries=val; + } + } + break; + + case C_RAND_RECS: + ASSIGN_ON_OFF(global->rnd_recs, p,C_ON,"bad qualifier in randomize_recs= option."); + break; + + case NEG_TTL: + SCAN_TIMESECS(global->neg_ttl, p,"neg_ttl option"); + break; + + case NEG_RRS_POL: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_DEFAULT || cnst==C_AUTH, + "bad qualifier in neg_rrs_pol= option."); + global->neg_rrs_pol=cnst; + } + break; + + case NEG_DOMAIN_POL: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_AUTH,"bad qualifier in neg_domain_pol= option."); + global->neg_domain_pol=cnst; + } + break; + + case QUERY_PORT_START: { + int val; + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_NONE) + val=-1; + else + goto bad_port_start_option; + } + else if(isdigit(*p)) { + val=strtol(p,&p,0); + if(val>65535) { + REPORT_ERROR("value for query_port_start out of range."); + PARSERROR; + } + else if(val<1024) + fprintf(stderr,"Warning: query_port_start=%i but source ports <1204 can only be used as root.\n", + val); + } + else { + bad_port_start_option: + REPORT_ERROR("bad qualifier in query_port_start= option."); + PARSERROR; + } + global->query_port_start=val; + } + break; + + case QUERY_PORT_END: { + int val; + SCAN_UNSIGNED_NUM(val,p,"query_port_end option"); + if(val>65535) { + REPORT_ERROR("value for query_port_end out of range."); + PARSERROR; + } + global->query_port_end=val; + } + break; + + case UDP_BUFSIZE: { + int val; + SCAN_UNSIGNED_NUM(val,p,"udpbufsize"); + if(val<512 || val>65535-(20+8)) { + REPORT_ERROR("value for udpbufsize out of range."); + PARSERROR; + } + global->udpbufsize=val; + } + break; + + case DELEGATION_ONLY: + SCAN_STRING_LIST(&global->deleg_only_zones,p,strbuf,len,zone_add) + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') goto expected_closing_brace; + if (global->query_port_end < global->query_port_start) { + REPORT_ERROR("query_port_end may not be smaller than query_port_start."); + PARSERROR; + } + break; + + case SERVER: { + servparm_t server; + + if(!servers) { + REPORT_ERROR(in?"server section not allowed in include file": + "server section not allowed in eval string"); + PARSERROR; + } + + server=serv_presets; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_servparm(&server)) + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,server_options); + if(!option) { + REPORT_ERRORF("invalid option for server section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') CLEANUP_GOTO(expected_equals); + ++p; + SKIP_BLANKS(p); + + switch(option) { + case IP: + SCAN_STRING_LIST(&server.atup_a,p,strbuf,len,addr_add_); + break; + + case FILET: + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_resolv_conf(strbuf, &server.atup_a, &errmsg)) { + if(errmsg) {REPORT_ERROR(errmsg); free(errmsg);} + else *errstr=NULL; + PARSERROR; + } + } + break; + + case PORT: + SCAN_UNSIGNED_NUM(server.port,p,"port option"); + break; + + case SCHEME: + SCAN_STRING(p,strbuf,len); + STRNCP(server.scheme, strbuf,len, "scheme"); + break; + + case UPTEST: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_PING || cnst==C_NONE || cnst==C_IF || cnst==C_EXEC || cnst==C_DEV || cnst==C_DIALD || cnst==C_QUERY,"bad qualifier in uptest= option."); + server.uptest=cnst; + } + break; + + case TIMEOUT: + SCAN_TIMESECS(server.timeout,p,"timeout option"); + break; + + case PING_TIMEOUT: + SCAN_UNSIGNED_NUM(server.ping_timeout,p,"ping_timeout option"); + break; + + case PING_IP: + SCAN_STRING(p,strbuf,len); + { + const char *err; + if ((err=parse_ip(strbuf,&server.ping_a))) { + REPORT_ERRORF("%s for the ping_ip= option.",err); + PARSERROR; + } + } + break; + + case UPTEST_CMD: + SCAN_STRING(p,strbuf,len); + STRNDUP(server.uptest_cmd, strbuf,len); + SKIP_BLANKS(p); + if(*p==',') { + ++p; + SKIP_BLANKS(p); + SCAN_STRING(p,strbuf,len); + STRNCP(server.uptest_usr, strbuf,len, "second argument of uptest_cmd"); + } + break; + + case QUERY_TEST_NAME: + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + if(*p!='.' && *p!='-') { + cnst=lookup_const(ps,len); + if(cnst==C_NONE) { + if(server.query_test_name) + free(server.query_test_name); + server.query_test_name=NULL; + break; + } + } + p=ps; /* reset current char pointer and try again. */ + } + { + unsigned char tname[DNSNAMEBUFSIZE], *copy; + unsigned sz; + + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,tname); + sz=rhnlen(tname); + copy= malloc(sz); + if(!copy) { + OUTOFMEMERROR; + } + memcpy(copy,tname,sz); + if(server.query_test_name) + free(server.query_test_name); + server.query_test_name=copy; + } + break; + + case INTERVAL: + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_ONQUERY) { + server.interval=-1; + } + else if(cnst==C_ONTIMEOUT) { + server.interval=-2; + } + else { + goto bad_interval_option; + } + } + else if(isdigit(*p)) { + char *err; + server.interval=strtotime(p,&p,&err); + if(err) { + REPORT_ERRORF("bad time specification in interval= option: %s",err); + PARSERROR; + } + } + else { + bad_interval_option: + REPORT_ERROR("bad qualifier in interval= option."); + PARSERROR; + } + break; + + case INTERFACE: + SCAN_STRING(p,strbuf,len); + STRNCP(server.interface, strbuf,len, "interface"); + break; + + case DEVICE: + SCAN_STRING(p,strbuf,len); + STRNCP(server.device, strbuf,len, "device"); + break; + + case PURGE_CACHE: + ASSIGN_ON_OFF(server.purge_cache,p,C_ON,"bad qualifier in purge_cache= option."); + break; + + case CACHING: + ASSIGN_ON_OFF(server.nocache,p,C_OFF,"bad qualifier in caching= option."); + break; + + case LEAN_QUERY: + ASSIGN_ON_OFF(server.lean_query,p,C_ON,"bad qualifier in lean_query= option."); + break; + + case EDNS_QUERY: + ASSIGN_ON_OFF(server.edns_query,p,C_ON,"bad qualifier in edns_query= option."); + break; + + case PRESET: + ASSIGN_ON_OFF(server.preset,p,C_ON,"bad qualifier in preset= option."); + break; + + case PROXY_ONLY: + ASSIGN_ON_OFF(server.is_proxy,p,C_ON,"bad qualifier in proxy_only= option."); + break; + + case ROOT_SERVER: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_DISCOVER,"bad qualifier in root_server= option."); + server.rootserver= (cnst==C_DISCOVER? 2: cnst==C_ON); + } + break; + + case RANDOMIZE_SERVERS: + ASSIGN_ON_OFF(server.rand_servers,p,C_ON,"bad qualifier in randomize_servers= option."); + break; + + case POLICY: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_INCLUDED || cnst==C_EXCLUDED || cnst==C_SIMPLE_ONLY || cnst==C_FQDN_ONLY,"bad qualifier in policy= option."); + server.policy=cnst; + } + break; + + case INCLUDE: + SCAN_STRING_LIST(&server.alist,p,strbuf,len,include_list_add) + break; + + case EXCLUDE: + SCAN_STRING_LIST(&server.alist,p,strbuf,len,exclude_list_add) + break; + + case REJECTLIST: + SCAN_STRING_LIST(&server,p,strbuf,len,reject_add_); + break; + + case REJECTPOLICY: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_FAIL || cnst==C_NEGATE,"bad qualifier in reject_policy= option."); + server.rejectpolicy=cnst; + } + break; + + case REJECTRECURSIVELY: + ASSIGN_ON_OFF(server.rejectrecursively,p,C_ON,"bad qualifier in reject_recursively= option."); + break; + + case LABEL: + SCAN_STRING(p,strbuf,len); + STRNDUP(server.label,strbuf,len); + break; + + default: /* we should never get here */ + CLEANUP_GOTO(internal_parse_error); + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') CLEANUP_GOTO(expected_semicolon); + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') CLEANUP_GOTO(expected_closing_brace); + if (server.uptest==C_EXEC) { + if (!server.uptest_cmd) { + REPORT_ERROR("you must specify uptest_cmd if you specify uptest=exec!"); + PARSERROR; + } + } + if (server.is_proxy && server.rootserver) { + REPORT_ERROR("A server may not be specified as both a proxy and a root-server."); + PARSERROR; + } + if(server.rootserver && (server.policy==C_SIMPLE_ONLY || server.policy==C_FQDN_ONLY)) + fprintf(stderr,"Warning: using policy=%s with a root-server usually makes no sense.", + const_name(server.policy)); + if (DA_NEL(server.atup_a)) { + check_localaddrs(&server); + if(!DA_NEL(server.atup_a)) { + REPORT_ERROR("Server section contains only local IP addresses.\n" + "Bind pdnsd to a different local IP address or specify different port numbers" + " in global section and server section if you want pdnsd to query servers on" + " the same machine."); + PARSERROR; + } + } + { + int j,n=DA_NEL(server.atup_a); + for(j=0;jis_up=server.preset; + /* A negative test interval means don't test at startup or reconfig. */ + if(server.interval<0) at->i_ts=time(NULL); + } + } + if(server.interval==-1) global->onquery=1; + + if (!(*servers=DA_GROW1_F(*servers,(void(*)(void*))free_servparm))) { + OUTOFMEMERROR; + } + DA_LAST(*servers)= server; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + break; + + case RR: { + /* Initialize c_cent to all zeros. + Then it should be safe to call free_cent() on it, even before calling init_cent(). */ + dns_cent_t c_cent={0}; + time_t c_ttl=86400; + unsigned c_flags=DF_LOCAL; + unsigned char reverse=0; + +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_cent(&c_cent DBG0)) + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,rr_options); + if(!option) { + REPORT_ERRORF("invalid option for rr section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') CLEANUP_GOTO(expected_equals); + ++p; + SKIP_BLANKS(p); + + switch(option) { + int tp; const char *tpname; + case NAME: { + unsigned char c_name[DNSNAMEBUFSIZE]; + if (c_cent.qname) { + REPORT_ERROR("You may specify only one name in a rr section."); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + if (!init_cent(&c_cent, c_name, 0, 0, c_flags DBG0)) + goto out_of_memory; + } + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p, "ttl option"); + break; + + case AUTHREC: { + int cnst; + if (c_cent.qname) { + REPORT_ERROR("The authrec= option has no effect unless it precedes name= in a rr section."); + PARSERROR; + } + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"Bad qualifier in authrec= option."); + c_flags=(cnst==C_ON)?DF_LOCAL:0; + } + break; + + case REVERSE: + ASSIGN_ON_OFF(reverse,p,C_ON,"bad qualifier in reverse= option."); + break; + + case A: { + unsigned int sz; + pdnsd_ca c_a; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + if (inet_aton(strbuf,&c_a.ipv4)) { + tp=T_A; + sz=sizeof(struct in_addr); + } + else +#if ALLOW_LOCAL_AAAA + if (inet_pton(AF_INET6,strbuf,&c_a.ipv6)>0) { + tp=T_AAAA; + sz=sizeof(struct in6_addr); + } + else +#endif + { + REPORT_ERROR("bad IP address in a= option."); + PARSERROR; + } + + if(!add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,sz,&c_a DBG0)) + goto add_rr_failed; + } + break; + + case OWNER: + tp=T_NS; + goto scan_name; + case CNAME: + tp=T_CNAME; + goto scan_name; + case PTR: + tp=T_PTR; + scan_name: + { + unsigned char c_name[DNSNAMEBUFSIZE]; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + if(!add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,rhnlen(c_name),c_name DBG0)) + goto add_rr_failed; + } + break; + + case MX: { + unsigned char *cp; + unsigned pref; + unsigned char c_mx[2+DNSNAMEBUFSIZE]; + + if (!c_cent.qname) + goto no_name_spec; + cp=c_mx+2; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,cp); + SKIP_COMMA(p,"missing second argument (preference level) of mx= option"); + SCAN_UNSIGNED_NUM(pref,p,"second argument of mx= option"); + cp=c_mx; + PUTINT16(pref,cp); + if(!add_cent_rr(&c_cent,T_MX,c_ttl,0,CF_LOCAL,2+rhnlen(cp),c_mx DBG0)) + goto add_rr_failed; + } + break; + + case SOA: { + unsigned int blen,rlen; + unsigned char *bp; + uint32_t val; + unsigned char buf[2*DNSNAMEBUFSIZE+20]; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,buf); + rlen=rhnlen(buf); + blen=rlen; + bp=buf+rlen; + SKIP_COMMA(p,"missing 2nd argument of soa= option"); + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,bp); + rlen=rhnlen(bp); + blen += rlen; + bp += rlen; + SKIP_COMMA(p,"missing 3rd argument of soa= option"); + SCAN_UNSIGNED_NUM(val,p,"3rd argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 4th argument of soa= option"); + SCAN_TIMESECS(val,p,"4th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 5th argument of soa= option"); + SCAN_TIMESECS(val,p,"5th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 6th argument of soa= option"); + SCAN_TIMESECS(val,p,"6th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 7th argument of soa= option"); + SCAN_TIMESECS(val,p,"7th argument of soa= option"); + PUTINT32(val,bp); + blen += 20; + if(!add_cent_rr(&c_cent,T_SOA,c_ttl,0,CF_LOCAL,blen,buf DBG0)) + goto add_rr_failed; + } + break; + case SPF: +#if IS_CACHED_SPF + tp=T_SPF; tpname="spf"; + goto define_txt_rr; +#else + REPORT_ERROR("Missing support for caching SPF records in rr section"); + PARSERROR; +#endif + case TXT: +#if IS_CACHED_TXT + tp=T_TXT; tpname="txt"; +#else + REPORT_ERROR("Missing support for caching TXT records in rr section"); + PARSERROR; +#endif +#if IS_CACHED_TXT || IS_CACHED_SPF +#if IS_CACHED_SPF + define_txt_rr: +#endif + { + unsigned char *rbuf; + unsigned sz,allocsz; + int rv; + + if (!c_cent.qname) + goto no_name_spec; + rbuf=NULL; + sz=allocsz=0; +# undef CLEANUP_HANDLER2 +# define CLEANUP_HANDLER2 (free(rbuf)) + + for(;;) { + unsigned char *newbuf,*cp; + unsigned newsz=sz+256; + int n; + if(newsz>allocsz) { + allocsz += 512; + newbuf=realloc(rbuf,allocsz); + if(!newbuf) { + OUTOFMEMERROR; + } + rbuf=newbuf; + } + cp = rbuf+sz; + n=scan_string(&p, charp (cp+1), 255, &scanstrerr); + if(n==-1) { + REPORT_ERRORF("%s in %s= option", scanstrerr, tpname); + PARSERROR; + } + if(n>255) { + REPORT_ERRORF("string longer than 255 bytes in %s= option", tpname); + PARSERROR; + } + *cp=n; + sz += n+1; + if(sz>0xffff) { + REPORT_ERRORF("data exceeds maximum size (65535 bytes) in %s= option", tpname); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!=',') break; + ++p; + SKIP_BLANKS(p); + } + rv=add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,sz,rbuf DBG0); + CLEANUP_HANDLER2; +# undef CLEANUP_HANDLER2 +# define CLEANUP_HANDLER2 + if(!rv) + goto add_rr_failed; + } + break; +#endif + default: /* we should never get here */ + CLEANUP_GOTO(internal_parse_error); + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') CLEANUP_GOTO(expected_semicolon); + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') CLEANUP_GOTO(expected_closing_brace); + if (!c_cent.qname) + goto no_name_spec; + if(c_cent.qname[0]==1 && c_cent.qname[1]=='*') { + /* Wild card record. Set the DF_WILD flag for the name with '*.' removed. */ + if(!set_cent_flags(&c_cent.qname[2],DF_WILD)) { + unsigned char buf[DNSNAMEBUFSIZE]; + rhn2str(c_cent.qname,buf,sizeof(buf)); + REPORT_ERRORF("You must define some records for '%s'" + " before you can define records for the wildcard name '%s'", + &buf[2],buf); + PARSERROR; + } + } + + add_cache(&c_cent); + if(reverse) { + if(!add_reverse_cache(&c_cent)) { + REPORT_ERROR("Can't convert IP address in a= option" + " into form suitable for reverse resolving."); + PARSERROR; + } + } + CLEANUP_HANDLER; + break; + + add_rr_failed: + OUTOFMEMERROR; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + + case SOURCE: { + unsigned char c_owner[DNSNAMEBUFSIZE]; + time_t c_ttl; + unsigned c_flags; + unsigned char c_aliases; + + c_owner[0]='\0'; + c_ttl=86400; + c_flags=DF_LOCAL; + c_aliases=0; + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,source_options); + if(!option) { + REPORT_ERRORF("invalid option for source section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case OWNER: + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_owner); + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p,"ttl option"); + break; + + case FILET: + if (!c_owner[0]) { + REPORT_ERROR("you must specify owner before file= in source records."); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_hosts(strbuf, c_owner, c_ttl, c_flags, c_aliases, &errmsg)) { + if(errmsg) { REPORT_ERROR(errmsg); free(errmsg); } + else *errstr=NULL; + PARSERROR; + } + } + break; + + case SERVE_ALIASES: + ASSIGN_ON_OFF(c_aliases,p,C_ON,"Bad qualifier in serve_aliases= option."); + break; + + case AUTHREC: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"Bad qualifier in authrec= option."); + c_flags=(cnst==C_ON)?DF_LOCAL:0; + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + case INCLUDE_F: { + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,include_options); + if(!option) { + REPORT_ERRORF("invalid option for include section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case FILET: + if(includedepth>=MAXINCLUDEDEPTH) { + REPORT_ERRORF("maximum include depth (%d) exceeded.",MAXINCLUDEDEPTH); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_config_file(strbuf, NULL, NULL, includedepth+1, &errmsg)) { + if(errmsg) { + if(linenr) { + if(asprintf(errstr, "In file %s included at line %u:\n%s",strbuf,linenr,errmsg)<0) + *errstr=NULL; + } + else { + if(asprintf(errstr, "In file %s:\n%s",strbuf,errmsg)<0) + *errstr=NULL; + } + free(errmsg); + } + else + *errstr=NULL; + PARSERROR; + } + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + case NEG: { + unsigned char c_name[DNSNAMEBUFSIZE]; + time_t c_ttl; + unsigned char htp,hdtp; + + htp=0; + hdtp=0; + c_name[0]='\0'; + c_ttl=86400; + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,neg_options); + if(!option) { + REPORT_ERRORF("invalid option for neg section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case NAME: + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p, "ttl option"); + break; + + case TYPES: + if (!c_name[0]) { + REPORT_ERROR("you must specify a name before the types= option."); + PARSERROR; + } + if (isalpha(*p)) { + int cnst; + dns_cent_t c_cent /* ={0} */; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_DOMAIN) { + if (htp) { + REPORT_ERROR("You may not specify types=domain together with other types!"); + PARSERROR; + } + hdtp=1; + if (!init_cent(&c_cent, c_name, c_ttl, 0, DF_LOCAL|DF_NEGATIVE DBG0)) + goto out_of_memory; + } + else if(cnst==0) { + if (hdtp) { + REPORT_ERROR("You may not specify types=domain together with other types!"); + PARSERROR; + } + htp=1; + if (!init_cent(&c_cent, c_name, 0, 0, 0 DBG0)) + goto out_of_memory; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_cent(&c_cent DBG0)) + for(;;) { + { + TEMPSTRNCPY(buf,ps,len); + cnst=rr_tp_byname(buf); + } + if(cnst==-1) { + REPORT_ERRORF("unrecognized rr type '%.*s' used as argument for types= option.",(int)len,ps); + PARSERROR; + } + if(PDNSD_NOT_CACHED_TYPE(cnst)) { + REPORT_ERRORF("illegal rr type '%.*s' used as argument for types= option.",(int)len,ps); + PARSERROR; + } + if (!getrrset_eff(&c_cent,cnst) && !add_cent_rrset_by_type(&c_cent,cnst,c_ttl,0,CF_LOCAL|CF_NEGATIVE DBG0)) { + OUTOFMEMERROR; + } + SKIP_BLANKS(p); + if(*p!=',') break; + ++p; + SKIP_BLANKS(p); + if (!isalpha(*p)) + {CLEANUP_GOTO(bad_types_option);} + SCAN_ALPHANUM(ps,p,len); + } + } + else + goto bad_types_option; + + add_cache(&c_cent); + CLEANUP_HANDLER; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + else { + bad_types_option: + REPORT_ERROR("Bad argument for types= option."); + PARSERROR; + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(sechdr) */ + + if(*p!='}') goto expected_closing_brace; + ++p; + } + else { + REPORT_ERROR("expected section header"); + PARSERROR; + } + } + + if(!in || feof(in)) { + if(getnextperr) { + REPORT_ERROR(getnextperr); + PARSERROR; + } + retval=1; /* success */ + } + else + goto input_error; + + goto free_linebuf_return; + + expected_bropen: + REPORT_ERROR("expected opening brace after section name"); + PARSERROR; + + expected_closing_brace: + REPORT_ERROR("expected beginning of new option or closing brace"); + PARSERROR; + + expected_equals: + REPORT_ERROR("expected equals sign after option name"); + PARSERROR; + + expected_semicolon: + REPORT_ERROR("too many arguments to option or missing semicolon"); + PARSERROR; + + string_err: + REPORT_ERROR(scanstrerr); + PARSERROR; + + string_too_long: + REPORT_ERROR("string length exceeds buffer size"); + PARSERROR; + + no_name_spec: + REPORT_ERROR("you must specify a name before a,ptr,cname,mx,ns(owner) and soa records."); + PARSERROR; + + internal_parse_error: + if(asprintf(errstr,"Internal inconsistency detected while parsing line %u of %s.\n" + "Please consider reporting this error to one of the maintainers.\n",linenr,conftype)<0) + *errstr=NULL; + PARSERROR; + + out_of_memory: + /* If malloc() just failed, allocating space for an error message is unlikely to succeed. */ + *errstr=NULL; + PARSERROR; + + unexpected_eof: + if(!in || feof(in)) { + REPORT_ERROR(getnextperr?getnextperr:in?"unexpected end of file":"unexpected end of input string"); + } + else + input_error: { + if(asprintf(errstr,"Error while reading config file: %s",strerror(errno))<0) + *errstr=NULL; + } + + free_linebuf_return: + free(linebuf); + return retval; + +#undef SKIP_BLANKS +#undef SCAN_STRING +#undef REPORT_ERROR +#undef REPORT_ERRORF +#undef PARSERROR +#undef OUTOFMEMERROR +#undef CLEANUP_GOTO +} + + +/* Convert a string representation of an IP address into a binary format. */ +static const char* parse_ip(const char *ipstr, pdnsd_a *a) +{ +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) cmdlineipv=-2; +#endif + { + if(!strcmp(ipstr,"any")) { +#ifdef ENABLE_IPV4 + if (run_ipv4) + a->ipv4.s_addr=INADDR_ANY; +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 + a->ipv6=in6addr_any; +#endif + } + else if(!str2pdnsd_a(ipstr,a)) { +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(run_ipv4 && inet_pton(AF_INET6,ipstr,&a->ipv6)>0) { + return "You should set run_ipv4=off or use the command-line option -6" + " before specifying an IPv6 address"; + } +#endif + return "bad IP address"; + } + } + return NULL; +} + +/* Add an IP address to the list of name servers. */ +static const char *addr_add(atup_array *ata, const char *ipstr) +{ + atup_t *at; + pdnsd_a addr; + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) cmdlineipv=-2; +#endif + { + if(!str2pdnsd_a(ipstr,&addr)) { +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(run_ipv4 && inet_pton(AF_INET6,ipstr,&addr.ipv6)>0) { + fprintf(stderr,"IPv6 address \"%s\" in config file ignored while running in IPv4 mode.\n",ipstr); + return NULL; + } +#endif + return "bad IP address"; + } + } + + if (!(*ata=DA_GROW1(*ata))) { + return "out of memory!"; + } + at=&DA_LAST(*ata); + SET_PDNSD_A2(&at->a, &addr); + at->is_up=0; + at->i_ts=0; + return NULL; +} + + +/* Helper functions for making netmasks */ +inline static uint32_t mk_netmask4(int len) +{ + uint32_t m; + + if(len<=0) + return 0; + + m= ~(uint32_t)0; + return (len<32)? htonl(m<<(32-len)): m; +} + +#if ALLOW_LOCAL_AAAA +inline static void mk_netmask6(struct in6_addr *m, int len) +{ + uint32_t *ma = (uint32_t *)m; + ma[0] = mk_netmask4(len); + ma[1] = mk_netmask4(len -= 32); + ma[2] = mk_netmask4(len -= 32); + ma[3] = mk_netmask4(len -= 32); +} +#endif + +/* Add an IP address/mask to the reject lists. */ +static const char *reject_add(servparm_t *serv, const char *ipstr) +{ + char *slash=strchr(ipstr,'/'); int mlen=0; + + if(slash) { + *slash++=0; + + if(*slash && isdigit(*slash)) { + char *endptr; + int l = strtol(slash,&endptr,10); + if(!*endptr) { + mlen=l; + slash=NULL; + } + } + } + else + mlen=128; /* Works for both IPv4 and IPv6 */ + + { + addr4maskpair_t am; + + am.mask.s_addr = mk_netmask4(mlen); + if(inet_aton(ipstr,&am.a) && (!slash || inet_aton(slash,&am.mask))) { + if(!(serv->reject_a4=DA_GROW1(serv->reject_a4))) + return "out of memory!"; + + DA_LAST(serv->reject_a4) = am; + return NULL; + } + } +#if ALLOW_LOCAL_AAAA + { + addr6maskpair_t am; + + mk_netmask6(&am.mask,mlen); + if(inet_pton(AF_INET6,ipstr,&am.a)>0 && (!slash || inet_pton(AF_INET6,slash,&am.mask)>0)) { + if(!(serv->reject_a6=DA_GROW1(serv->reject_a6))) + return "out of memory!"; + + DA_LAST(serv->reject_a6) = am; + return NULL; + } + } +#endif + + return "bad IP address"; +} + +/* Try to avoid the possibility that pdnsd will query itself. */ +static void check_localaddrs(servparm_t *serv) +{ + if(serv->port == global.port) { + atup_array ata=serv->atup_a; + int i,j=0,n=DA_NEL(ata); + for(i=0;ia))) { + char buf[ADDRSTR_MAXLEN]; + fprintf(stderr,"Local name-server address \"%s\" ignored in config file.\n", + pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN)); + continue; + } + } + else { + if(equiv_inaddr2(&global.a,&at->a)) { + char buf[ADDRSTR_MAXLEN]; + fprintf(stderr,"Ignoring name-server address \"%s\" in config file (identical to server_ip address).\n", + pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN)); + continue; + } + } + if(jatup_a=DA_RESIZE(ata,j); + } +} + +/* Read the name server addresses from a resolv.conf-style file. */ +static int read_resolv_conf(const char *fn, atup_array *ata, char **errstr) +{ + int rv=0; + FILE *f; + char *buf; + size_t buflen=256; + unsigned linenr=0; + + if (!(f=fopen(fn,"r"))) { + if(asprintf(errstr, "Failed to open %s: %s", fn, strerror(errno))<0) + *errstr=NULL; + return 0; + } + buf=malloc(buflen); + if(!buf) { + *errstr=NULL; + goto fclose_return; + } + while(getline(&buf,&buflen,f)>=0) { + size_t len; + char *p,*ps; + ++linenr; + p=buf; + for(;; ++p) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + } + ps=p; + do { + if(!*++p) goto nextline; + } while(!isspace(*p)); + len=p-ps; + if(len==strlitlen("nameserver") && !strncmp(ps,"nameserver",len)) { + const char *errmsg; + do { + if(!*++p) goto nextline; + } while (isspace(*p)); + ps=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-ps; + { + TEMPSTRNCPY(ipstr,ps,len); + errmsg=addr_add(ata, ipstr); + } + if(errmsg) { + if(asprintf(errstr, "%s in line %u of file %s", errmsg,linenr,fn)<0) + *errstr=NULL; + goto cleanup_return; + } + } + nextline:; + } + if (feof(f)) + rv=1; + else if(asprintf(errstr, "Failed to read %s: %s", fn, strerror(errno))<0) + *errstr=NULL; + cleanup_return: + free(buf); + fclose_return: + fclose(f); + return rv; +} + +static const char *slist_add(slist_array *sla, const char *nm, unsigned int len, int tp) +{ + slist_t *sl; + int exact=1; + const char *err; + size_t sz; + unsigned char rhn[DNSNAMEBUFSIZE]; + + if (len>1 && *nm=='.') { + exact=0; + ++nm; + --len; + } + if((err=parsestr2rhn(ucharp nm,len,rhn))) + return err; + sz=rhnlen(rhn); + if (!(*sla=DA_GROW1_F(*sla,free_slist_domain))) { + return "out of memory!"; + } + sl=&DA_LAST(*sla); + + sl->exact=exact; + sl->rule=tp; + if (!(sl->domain=malloc(sz))) + return "out of memory!"; + memcpy(sl->domain,rhn,sz); + return NULL; +} + +static const char *zone_add(zone_array *za, const char *zone, unsigned int len) +{ + zone_t z; + const char *err; + size_t sz; + unsigned char rhn[DNSNAMEBUFSIZE]; + + if((err=parsestr2rhn(ucharp zone,len,rhn))) + return err; + sz=rhnlen(rhn); + if(!(*za=DA_GROW1_F(*za,free_zone)) || !(DA_LAST(*za)=z=malloc(sz))) + return "out of memory!"; + memcpy(z,rhn,sz); + return NULL; +} + diff --git a/app/src/main/jni/pdnsd/src/conf-parser.h b/app/src/main/jni/pdnsd/src/conf-parser.h new file mode 100644 index 0000000..d3a3e99 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/conf-parser.h @@ -0,0 +1,29 @@ +/* conf-parser.h - definitions for parser of pdnsd config files. + The parser was rewritten in C from scratch and doesn't require (f)lex + or yacc/bison. + + Copyright (C) 2004,2008 Paul A. Rombouts. + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef CONF_PARSER_H +#define CONF_PARSER_H + +int confparse(FILE* in, char *prestr, globparm_t *global, servparm_array *servers, int includedepth, char **errstr); + +#endif /* CONF_PARSER_H */ diff --git a/app/src/main/jni/pdnsd/src/conff.c b/app/src/main/jni/pdnsd/src/conff.c new file mode 100644 index 0000000..db6c5d1 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/conff.c @@ -0,0 +1,544 @@ +/* conff.c - Maintain configuration information + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "conff.h" +#include "consts.h" +#include "helpers.h" +#include "conf-parser.h" +#include "servers.h" +#include "icmp.h" + + +globparm_t global={ + perm_cache: 2048, + cache_dir: NULL, + pidfile: NULL, + port: 53, + a: PDNSD_A_INITIALIZER, + out_a: PDNSD_A_INITIALIZER, +#ifdef ENABLE_IPV6 + ipv4_6_prefix: IN6ADDR_ANY_INIT, +#endif + max_ttl: 604800, + min_ttl: 120, + neg_ttl: 900, + neg_rrs_pol: C_DEFAULT, + neg_domain_pol: C_AUTH, + verbosity: VERBOSITY, + run_as: "", + daemon: 0, + debug: 0, + stat_pipe: 0, + notcp: 0, + strict_suid: 1, + use_nss: 1, + paranoid: 0, + lndown_kluge: 0, + onquery: 0, + rnd_recs: 1, + ctl_perms: 0600, + scheme_file: NULL, + proc_limit: 40, + procq_limit: 60, + tcp_qtimeout: TCP_TIMEOUT, + timeout: 0, + par_queries: PAR_QUERIES, + query_method: M_PRESET, + query_port_start: 1024, + query_port_end: 65535, + udpbufsize: 1024, + deleg_only_zones: NULL +}; + +servparm_t serv_presets={ + port: 53, + uptest: C_NONE, + timeout: 120, + interval: 900, + ping_timeout: 600, + scheme: "", + uptest_cmd: NULL, + uptest_usr: "", + interface: "", + device: "", + query_test_name: NULL, + label: NULL, + purge_cache: 0, + nocache: 0, + lean_query: 1, + edns_query: 0, + is_proxy: 0, + rootserver: 0, + rand_servers: 0, + preset: 1, + rejectrecursively: 0, + rejectpolicy: C_FAIL, + policy: C_INCLUDED, + alist: NULL, + atup_a: NULL, + reject_a4: NULL, +#if ALLOW_LOCAL_AAAA + reject_a6: NULL, +#endif + ping_a: PDNSD_A_INITIALIZER +}; + +servparm_array servers=NULL; + +static void free_zones(zone_array za); +static void free_server_data(servparm_array sa); +static int report_server_stat(int f,int i); + + +/* + * Read a configuration file, saving the results in a (separate) global section and servers array, + * and the cache. + * + * char *nm should contain the name of the file to read. If it is NULL, the name of the config file + * read during startup is used. + * + * globparm_t *global should point to a struct which will be used to store the data of the + * global section(s). If it is NULL, no global sections are allowed in the + * file. + * + * servparm_array *servers should point to a dynamic array which will be grown to store the data + * of the server sections. If it is NULL, no server sections are allowed + * in the file. + * + * char **errstr is used to return a possible error message. + * In case of failure, *errstr will refer to a newly allocated string. + * + * read_config_file returns 1 on success, 0 on failure. + */ +int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr) +{ + int retval=0; + const char *conftype= (global?"config":"include"); + FILE *in; + + if (nm==NULL) + nm=conf_file; + + if (!(in=fopen(nm,"r"))) { + if(asprintf(errstr,"Error: Could not open %s file %s: %s",conftype,nm,strerror(errno))<0) + *errstr=NULL; + return 0; + } + + retval=confparse(in,NULL,global,servers,includedepth,errstr); +close_file: + if(fclose(in) && retval) { + if(asprintf(errstr,"Error: Could not close %s file %s: %s", + conftype,nm,strerror(errno))<0) + *errstr=NULL; + return 0; + } + if(retval && servers && !DA_NEL(*servers)) { + if(asprintf(errstr,"Error: no server sections defined in config file %s",nm)<0) + *errstr=NULL; + return 0; + } + return retval; +} + +/* + * Re-Read the configuration file. + * Return 1 on success, 0 on failure. + * In case of failure, the old configuration will be unchanged (although the cache may not) and + * **errstr will refer to a newly allocated string containing an error message. + */ +int reload_config_file(const char *nm, char **errstr) +{ + globparm_t global_new; + servparm_array servers_new; + + global_new=global; + global_new.cache_dir=NULL; + global_new.pidfile=NULL; + global_new.scheme_file=NULL; + global_new.deleg_only_zones=NULL; + global_new.onquery=0; + servers_new=NULL; + if(read_config_file(nm,&global_new,&servers_new,0,errstr)) { + if(global_new.cache_dir && strcmp(global_new.cache_dir,global.cache_dir)) { + *errstr=strdup("Cannot reload config file: the specified cache_dir directory has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.pidfile && (!global.pidfile || strcmp(global_new.pidfile,global.pidfile))) { + *errstr=strdup("Cannot reload config file: the specified pid_file has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.scheme_file && strcmp(global_new.scheme_file,global.scheme_file)) { + *errstr=strdup("Cannot reload config file: the specified scheme_file has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.port!=global.port) { + *errstr=strdup("Cannot reload config file: the specified server_port has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(!ADDR_EQUIV(&global_new.a,&global.a)) { + *errstr=strdup("Cannot reload config file: the specified interface address (server_ip) has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } +#ifdef ENABLE_IPV6 + if(!IN6_ARE_ADDR_EQUAL(&global_new.ipv4_6_prefix,&global.ipv4_6_prefix)) { + *errstr=strdup("Cannot reload config file: the specified ipv4_6_prefix has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } +#endif + if(strcmp(global_new.run_as,global.run_as)) { + *errstr=strdup("Cannot reload config file: the specified run_as id has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.daemon!=global.daemon) { + *errstr=strdup("Cannot reload config file: the daemon option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.debug!=global.debug) { + *errstr=strdup("Cannot reload config file: the debug option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.stat_pipe!=global.stat_pipe) { + *errstr=strdup("Cannot reload config file: the status_ctl option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.notcp!=global.notcp) { + *errstr=strdup("Cannot reload config file: the tcp_server option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.strict_suid!=global.strict_suid) { + *errstr=strdup("Cannot reload config file: the strict_setuid option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.ctl_perms!=global.ctl_perms) { + *errstr=strdup("Cannot reload config file: the specified ctl_perms has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(ping_isocket==-1 +#ifdef ENABLE_IPV6 + && ping6_isocket==-1 +#endif + ) { + int i,n=DA_NEL(servers_new); + for (i=0;idomain); +} + +void free_slist_array(slist_array sla) +{ + int j,m=DA_NEL(sla); + for(j=0;juptest_cmd); + free(serv->query_test_name); + free(serv->label); + da_free(serv->atup_a); + free_slist_array(serv->alist); + da_free(serv->reject_a4); +#if ALLOW_LOCAL_AAAA + da_free(serv->reject_a6); +#endif +} + +static void free_server_data(servparm_array sa) +{ + int i,n=DA_NEL(sa); + for(i=0;ireject_a4!=NULL || (s)->reject_a6!=NULL) +#else +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL) +#endif + + +/* Report the current status of server i to the file descriptor f. + Call with locks applied. +*/ +static int report_server_stat(int f,int i) +{ + servparm_t *st=&DA_INDEX(servers,i); + int j,m; + + fsprintf_or_return(f,"Server %i:\n------\n",i); + fsprintf_or_return(f,"\tlabel: %s\n",st->label?st->label:"(none)"); + m=DA_NEL(st->atup_a); + if(st->rootserver>1 && m) + fsprintf_or_return(f,"\tThe following name servers will be used for discovery of rootservers only:\n"); + for(j=0;jatup_a,j); + {char buf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\tip: %s\n",pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN));} + fsprintf_or_return(f,"\tserver assumed available: %s\n",at->is_up?"yes":"no"); + } + fsprintf_or_return(f,"\tport: %hu\n",st->port); + fsprintf_or_return(f,"\tuptest: %s\n",const_name(st->uptest)); + fsprintf_or_return(f,"\ttimeout: %li\n",(long)st->timeout); + if(st->interval>0) { + fsprintf_or_return(f,"\tuptest interval: %li\n",(long)st->interval); + } else { + fsprintf_or_return(f,"\tuptest interval: %s\n", + st->interval==-1?"onquery": + st->interval==-2?"ontimeout": + "(never retest)"); + } + fsprintf_or_return(f,"\tping timeout: %li\n",(long)st->ping_timeout); + {char buf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\tping ip: %s\n",is_inaddr_any(&st->ping_a)?"(using server ip)":pdnsd_a2str(&st->ping_a,buf,ADDRSTR_MAXLEN));} + if(st->interface[0]) { + fsprintf_or_return(f,"\tinterface: %s\n",st->interface); + } + if(st->device[0]) { + fsprintf_or_return(f,"\tdevice (for special Linux ppp device support): %s\n",st->device); + } + if(st->uptest_cmd) { + fsprintf_or_return(f,"\tuptest command: %s\n",st->uptest_cmd); + fsprintf_or_return(f,"\tuptest user: %s\n",st->uptest_usr[0]?st->uptest_usr:"(process owner)"); + } + if(st->query_test_name) { + unsigned char nmbuf[DNSNAMEBUFSIZE]; + fsprintf_or_return(f,"\tname used in query uptest: %s\n", + rhn2str(st->query_test_name,nmbuf,sizeof(nmbuf))); + } + if (st->scheme[0]) { + fsprintf_or_return(f,"\tscheme: %s\n", st->scheme); + } + fsprintf_or_return(f,"\tforce cache purging: %s\n",st->purge_cache?"on":"off"); + fsprintf_or_return(f,"\tserver is cached: %s\n",st->nocache?"off":"on"); + fsprintf_or_return(f,"\tlean query: %s\n",st->lean_query?"on":"off"); + fsprintf_or_return(f,"\tUse EDNS in outgoing queries: %s\n",st->edns_query?"on":"off"); + fsprintf_or_return(f,"\tUse only proxy?: %s\n",st->is_proxy?"on":"off"); + fsprintf_or_return(f,"\tAssumed root server: %s\n",st->rootserver?(st->rootserver==1?"yes":"discover"):"no"); + fsprintf_or_return(f,"\tRandomize server query order: %s\n",st->rand_servers?"yes":"no"); + fsprintf_or_return(f,"\tDefault policy: %s\n",const_name(st->policy)); + fsprintf_or_return(f,"\tPolicies:%s\n", st->alist?"":" (none)"); + for (j=0;jalist);++j) { + slist_t *sl=&DA_INDEX(st->alist,j); + unsigned char buf[DNSNAMEBUFSIZE]; + fsprintf_or_return(f,"\t\t%s: %s%s\n", + sl->rule==C_INCLUDED?"include":"exclude", + sl->exact?"":".", + rhn2str(sl->domain,buf,sizeof(buf))); + } + if(serv_has_rejectlist(st)) { + fsprintf_or_return(f,"\tAddresses which should be rejected in replies:\n"); + m=DA_NEL(st->reject_a4); + for (j=0;jreject_a4,j); + char abuf[ADDRSTR_MAXLEN],mbuf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\t\t%s/%s\n",inet_ntop(AF_INET,&am->a,abuf,sizeof(abuf)), + inet_ntop(AF_INET,&am->mask,mbuf,sizeof(mbuf))); + } +#if ALLOW_LOCAL_AAAA + m=DA_NEL(st->reject_a6); + for (j=0;jreject_a6,j); + char abuf[INET6_ADDRSTRLEN],mbuf[INET6_ADDRSTRLEN]; + fsprintf_or_return(f,"\t\t%s/%s\n",inet_ntop(AF_INET6,&am->a,abuf,sizeof(abuf)), + inet_ntop(AF_INET6,&am->mask,mbuf,sizeof(mbuf))); + } +#endif + fsprintf_or_return(f,"\tReject policy: %s\n",const_name(st->rejectpolicy)); + fsprintf_or_return(f,"\tReject recursively: %s\n",st->rejectrecursively?"yes":"no"); + } + return 0; +} diff --git a/app/src/main/jni/pdnsd/src/conff.h b/app/src/main/jni/pdnsd/src/conff.h new file mode 100644 index 0000000..a07b156 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/conff.h @@ -0,0 +1,190 @@ +/* conff.h - Definitions for configuration management. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef CONFF_H +#define CONFF_H + +/* XXX should use the system defined ones. */ +/* #define MAXPATH 1024 */ +/* #define MAXIFNAME 31 */ + +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "list.h" + +/* From main.c */ +#if DEBUG>0 +extern short int debug_p; +#else +#define debug_p 0 +#endif +extern short int stat_pipe; +extern pthread_t main_thrid; +extern uid_t init_uid; +extern char *conf_file; + +/* ----------- */ + +typedef DYNAMIC_ARRAY(pdnsd_a) *addr_array; +typedef DYNAMIC_ARRAY(pdnsd_a2) *addr2_array; + +typedef struct { + time_t i_ts; + char is_up; + pdnsd_a2 a; +} atup_t; +typedef DYNAMIC_ARRAY(atup_t) *atup_array; + +typedef struct { + unsigned char *domain; + short exact; + short rule; +} slist_t; +typedef DYNAMIC_ARRAY(slist_t) *slist_array; + +typedef struct { + struct in_addr a,mask; +} addr4maskpair_t; + +typedef DYNAMIC_ARRAY(addr4maskpair_t) *a4_array; + +#if ALLOW_LOCAL_AAAA +typedef struct { + struct in6_addr a,mask; +} addr6maskpair_t; + +typedef DYNAMIC_ARRAY(addr6maskpair_t) *a6_array; +#endif + +typedef struct { + unsigned short port; + short uptest; + time_t timeout; + time_t interval; + time_t ping_timeout; + char scheme[32]; + char *uptest_cmd; + char uptest_usr[21]; + char interface[IFNAMSIZ]; + char device[IFNAMSIZ]; + unsigned char *query_test_name; + char *label; + char purge_cache; + char nocache; + char lean_query; + char edns_query; + char is_proxy; + char rootserver; + char rand_servers; + char preset; + char rejectrecursively; + short rejectpolicy; + short policy; + slist_array alist; + atup_array atup_a; + a4_array reject_a4; +#if ALLOW_LOCAL_AAAA + a6_array reject_a6; +#endif + pdnsd_a ping_a; +} servparm_t; +typedef DYNAMIC_ARRAY(servparm_t) *servparm_array; + +typedef unsigned char *zone_t; +typedef DYNAMIC_ARRAY(zone_t) *zone_array; + +typedef struct { + long perm_cache; + char *cache_dir; + char *pidfile; + int port; + pdnsd_a a; + pdnsd_a out_a; +#ifdef ENABLE_IPV6 + struct in6_addr ipv4_6_prefix; +#endif + time_t max_ttl; + time_t min_ttl; + time_t neg_ttl; + short neg_rrs_pol; + short neg_domain_pol; + short verbosity; + char run_as[21]; + char daemon; + char debug; + char stat_pipe; + char notcp; + char strict_suid; + char use_nss; + char paranoid; + char lndown_kluge; + char onquery; + char rnd_recs; + int ctl_perms; + char *scheme_file; + int proc_limit; + int procq_limit; + time_t tcp_qtimeout; + time_t timeout; + int par_queries; + int query_method; + int query_port_start; + int query_port_end; + int udpbufsize; + zone_array deleg_only_zones; +} globparm_t; + +typedef struct { + char +#ifdef ENABLE_IPV6 + prefix, +#endif + pidfile, + verbosity, + pdnsduser, + daemon, + debug, + stat_pipe, + notcp, + query_method; +} cmdlineflags_t; + +extern globparm_t global; +extern cmdlineflags_t cmdline; +extern servparm_t serv_presets; + +extern servparm_array servers; + +int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr); +int reload_config_file(const char *nm, char **errstr); +void free_zone(void *ptr); +void free_slist_domain(void *ptr); +void free_slist_array(slist_array sla); +void free_servparm(servparm_t *serv); + +int report_conf_stat(int f); +#endif diff --git a/app/src/main/jni/pdnsd/src/consts.c b/app/src/main/jni/pdnsd/src/consts.c new file mode 100644 index 0000000..c875a09 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/consts.c @@ -0,0 +1,133 @@ +/* consts.c - Common config constants & handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2006, 2007, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include "consts.h" +#include "rr_types.h" + + +/* Order alphabetically!! */ +static const namevalue_t const_dic[]={ + {"auth", C_AUTH}, + {"default", C_DEFAULT}, + {"dev", C_DEV}, + {"diald", C_DIALD}, + {"discover", C_DISCOVER}, + {"domain", C_DOMAIN}, + {"excluded", C_EXCLUDED}, + {"exec", C_EXEC}, + {"fail", C_FAIL}, + {"false", C_OFF}, + {"fqdn_only", C_FQDN_ONLY}, + {"if", C_IF}, + {"included", C_INCLUDED}, + {"negate", C_NEGATE}, + {"no", C_OFF}, + {"none", C_NONE}, + {"off", C_OFF}, + {"on", C_ON}, + {"onquery", C_ONQUERY}, + {"ontimeout", C_ONTIMEOUT}, + {"ping", C_PING}, + {"query", C_QUERY}, + {"simple_only", C_SIMPLE_ONLY}, + {"tcp_only", TCP_ONLY}, + {"tcp_udp", TCP_UDP}, + {"true", C_ON}, + {"udp_only", UDP_ONLY}, + {"udp_tcp", UDP_TCP}, + {"yes", C_ON} +}; + +/* Added by Paul Rombouts */ +static const char *const const_names[]={ + "error", + "on", + "off", + "default", + "discover", + "none", + "if", + "exec", + "ping", + "query", + "onquery", + "ontimeout", + "udp_only", + "tcp_only", + "tcp_udp", + "udp_tcp", + "dev", + "diald", + "included", + "excluded", + "simple_only", + "fqdn_only", + "auth", + "domain", + "fail", + "negate" +}; + +/* compare two strings. + The first one is given as pointer to a char array of length len (which + should not contain any null chars), + the second one as a pointer to a null terminated char array. +*/ +inline static int keyncmp(const char *key1, int len, const char *key2) +{ + int cmp=strncmp(key1,key2,len); + if(cmp) return cmp; + return -(int)((unsigned char)(key2[len])); +} + +int binsearch_keyword(const char *name, int len, const namevalue_t dic[], int range) +{ + int i=0,j=range; + + while(i0) + i=k+1; + else + return dic[k].val; + } + + return 0; +} + + +int lookup_const(const char *name, int len) +{ + return binsearch_keyword(name,len,const_dic,sizeof(const_dic)/sizeof(namevalue_t)); +} + +/* Added by Paul Rombouts */ +const char *const_name(int c) +{ + return (c>=0 && c. +*/ + + +#ifndef CONSTS_H +#define CONSTS_H + +#include + +#define C_RRTOFFS 64 + +enum { + C_ERR, + C_ON, + C_OFF, + C_DEFAULT, + C_DISCOVER, + C_NONE, + C_IF, + C_EXEC, + C_PING, + C_QUERY, + C_ONQUERY, + C_ONTIMEOUT, + UDP_ONLY, + TCP_ONLY, + TCP_UDP, + UDP_TCP, + C_DEV, + C_DIALD, + C_INCLUDED, + C_EXCLUDED, + C_SIMPLE_ONLY, + C_FQDN_ONLY, + C_AUTH, + C_DOMAIN, + C_FAIL, + C_NEGATE +}; + +typedef struct { + const char *name; + int val; +} namevalue_t; + +int binsearch_keyword(const char *name, int len, const namevalue_t dic[], int range); +int lookup_const(const char *name, int len); +const char *const_name(int c); /* Added by Paul Rombouts */ + +#endif diff --git a/app/src/main/jni/pdnsd/src/debug.c b/app/src/main/jni/pdnsd/src/debug.c new file mode 100644 index 0000000..dd7f244 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/debug.c @@ -0,0 +1,64 @@ +/* debug.c - Various debugging facilities + * Copyright (C) 2001 Thomas Moestl + * + * This file is part of the pdnsd package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "helpers.h" +#include "error.h" + + +/* + * This is indeed very primitive (it does not track allocation failures + * and so on). It should be expanded some time. + */ +#ifdef ALLOC_DEBUG +void *DBGcalloc(size_t n, size_t sz, char *file, int line) +{ + DEBUG_MSG("+ calloc, %s:%d\n", file, line); + return calloc(n, sz); +} + +void *DBGmalloc(size_t sz, char *file, int line) +{ + DEBUG_MSG("+ malloc, %s:%d\n", file, line); + return malloc(sz); +} + +void *DBGrealloc(void *ptr, size_t sz, char *file, int line) +{ + if (ptr == NULL && sz != 0) + DEBUG_MSG("+ realloc, %s:%d\n", file, line); + if (ptr != NULL && sz == 0) + DEBUG_MSG("- realloc(0), %s:%d\n", file, line); + return realloc(ptr, sz); +} +void DBGfree(void *ptr, char *file, int line) +{ + DEBUG_MSG("- free, %s:%d\n", file, line); + free(ptr); +} +#endif diff --git a/app/src/main/jni/pdnsd/src/debug.h b/app/src/main/jni/pdnsd/src/debug.h new file mode 100644 index 0000000..02f0e5d --- /dev/null +++ b/app/src/main/jni/pdnsd/src/debug.h @@ -0,0 +1,52 @@ +/* debug.h - Various debugging facilities + * Copyright (C) 2001 Thomas Moestl + * + * This file is part of the pdnsd package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef DEBUG_H +#define DEBUG_H + +/* + * A hand-rolled alloc debug factility, because most available libraries have + * problems with at least one thread implementation. + */ +#ifdef ALLOC_DEBUG +void *DBGcalloc(size_t n, size_t sz, char *file, int line); +void *DBGmalloc(size_t sz, char *file, int line); +void *DBGrealloc(void *ptr, size_t sz, char *file, int line); +void DBGfree(void *ptr, char *file, int line); + +#define pdnsd_calloc(n,sz) DBGcalloc(n,sz,__FILE__,__LINE__) +#define pdnsd_malloc(sz) DBGmalloc(sz,__FILE__,__LINE__) +#define pdnsd_realloc(ptr,sz) DBGrealloc(ptr,sz,__FILE__,__LINE__) +#define pdnsd_free(ptr) DBGfree(ptr,__FILE__,__LINE__) +#else +#define pdnsd_calloc calloc +#define pdnsd_malloc malloc +#define pdnsd_realloc realloc +#define pdnsd_free free +#endif + +#endif /* def DEBUG_H */ diff --git a/app/src/main/jni/pdnsd/src/dns.c b/app/src/main/jni/pdnsd/src/dns.c new file mode 100644 index 0000000..a02d7fd --- /dev/null +++ b/app/src/main/jni/pdnsd/src/dns.c @@ -0,0 +1,617 @@ +/* dns.c - Declarations for dns handling and generic dns functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include "error.h" +#include "helpers.h" +#include "dns.h" + + +/* Decompress a name record, taking the whole message as msg, returning its results in tgt + * (which should be able hold at least DNSNAMEBUFSIZE chars), + * taking sz as the remaining msg size (it is returned decremented by the name length, ready for further use) and + * a source pointer (it is returned pointing to the location after the name). msgsize is the size of the whole message, + * len is the total name length. + * msg and msgsz are needed for decompression (see rfc1035). The returned data is decompressed, but still in the + * rr name form (length byte - string of that length, terminated by a 0 length byte). + * + * Returned is a dns return code, with one exception: RC_TRUNC, as defined in dns.h, indicates that the message is + * truncated at the name (which needs a special return code, as it might or might not be fatal). + */ +int decompress_name(unsigned char *msg, size_t msgsz, unsigned char **src, size_t *sz, unsigned char *tgt, unsigned int *len) +{ + unsigned int lb,offs; + unsigned int hops=0,tpos=0; + unsigned char *lptr=*src; + size_t oldsz=*sz; + size_t newsz=oldsz; + + if (newsz==0) + goto name_outside_data; + if (lptr-msg>=msgsz) + goto name_outside_msg; + + for(;;) { + newsz--; + lb=*lptr++; + + if(lb>0x3f) { + if (lb<0xc0) /* The two highest bits must be either 00 or 11 */ + goto unsupported_lbl_bits; + if (newsz==0) + goto name_outside_data; + if (lptr-msg>=msgsz) + goto name_outside_msg; + newsz--; + offs=((lb&0x3f)<<8)|(*lptr); + if (offs>=msgsz) + goto offset_outside_msg; + lptr=msg+offs; + goto jumped; + } + tgt[tpos++]=lb; + if (lb==0) + break; + + if (newsz<=lb) + goto name_outside_data; + if (lptr+lb-msg>=msgsz) + goto name_outside_msg; + if (tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */ + goto name_buf_full; + newsz -= lb; + do { + /* if (!*lptr || *lptr=='.') + return RC_FORMAT; */ + tgt[tpos++]=*lptr++; + } while(--lb); + } + goto return_OK; + + jumped: + ++hops; + for(;;) { + lb=*lptr++; + + while(lb>0x3f) { + if (lb<0xc0) /* The two highest bits must be either 00 or 11 */ + goto unsupported_lbl_bits; + if (lptr-msg>=msgsz) + goto name_outside_msg; + if (++hops>255) + goto too_many_hops; + offs=((lb&0x3f)<<8)|(*lptr); + if (offs>=msgsz) + goto offset_outside_msg; + lptr=msg+offs; + lb=*lptr++; + } + tgt[tpos++]=lb; + if (lb==0) + break; + + if (lptr+lb-msg>=msgsz) + goto name_outside_msg; + if(tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */ + goto name_buf_full; + do { + /* if (!*lptr || *lptr=='.') + return RC_FORMAT; */ + tgt[tpos++]=*lptr++; + } while(--lb); + } + return_OK: + *src += oldsz-newsz; + *sz = newsz; + if(len) *len=tpos; + return RC_OK; + + name_outside_data: + DEBUG_MSG("decompress_name: compressed name extends outside data field.\n"); + return RC_TRUNC; + + name_outside_msg: + DEBUG_MSG("decompress_name: compressed name extends outside message.\n"); + return RC_FORMAT; + + unsupported_lbl_bits: + DEBUG_MSG(lb==0x41?"decompress_name: Bit-string labels not supported.\n": + "decompress_name: unsupported label type.\n"); + return RC_FORMAT; + + offset_outside_msg: + DEBUG_MSG("decompress_name: offset points outside message.\n"); + return RC_FORMAT; + + name_buf_full: + DEBUG_MSG("decompress_name: decompressed name larger than %u bytes.\n", DNSNAMEBUFSIZE); + return RC_FORMAT; + + too_many_hops: + DEBUG_MSG("decompress_name: too many offsets in compressed name.\n"); + return RC_FORMAT; +} + +#if 0 +/* Compare two names (ordinary C-strings) back-to-forth and return the longest match. + The comparison is done at name granularity. + The return value is the length of the match in name elements. + *os (*od) is set to the offset in the domain name ms (md) of the match. + */ +int domain_name_match(const unsigned char *ms, const unsigned char *md, int *os, int *od) +{ + int i,j,k=0,offs,offd; + + offs=i=strlen(ms); offd=j=strlen(md); + if(i && ms[i-1]=='.') --offs; + if(j && md[j-1]=='.') --offd; + + if(i==0 || (i==1 && *ms=='.') || j==0 || (j==1 && *md=='.')) + /* Special case: root domain */ + ; + else { + --i; if(ms[i]=='.') --i; + --j; if(md[j]=='.') --j; + while(tolower(ms[i]) == tolower(md[j])) { + if(ms[i]=='.') { + ++k; + offs=i+1; offd=j+1; + } + if(i==0 || j==0) { + if((i==0 || ms[i-1]=='.') && (j==0 || md[j-1]=='.')) { + ++k; + offs=i; offd=j; + } + break; + } + --i; --j; + } + } + if(os) *os=offs; + if(od) *od=offd; + return k; +} +#endif + +/* Compare the names (in length byte-string notation) back-to-forth and return the longest match. + The comparison is done at name granularity. + The return value is the length of the match in name elements. + *os (*od) is set to the offset in the domain name ms (md) of the match. + */ +unsigned int domain_match(const unsigned char *ms, const unsigned char *md, unsigned int *os, unsigned int *od) +{ + unsigned int i,j,k,n,ns=0,nd=0,offs,offd; + unsigned char lb,ls[128],ld[128]; + + /* first collect all length bytes */ + i=0; + while((lb=ms[i])) { + PDNSD_ASSERT(ns<128, "domain_match: too many name segments"); + ls[ns++]=lb; + i += ((unsigned)lb)+1; + } + + j=0; + while((lb=md[j])) { + PDNSD_ASSERT(nd<128, "domain_match: too many name segments"); + ld[nd++]=lb; + j += ((unsigned)lb)+1; + } + + n=ns; if(n>nd) n=nd; + + for(k=1; offs=i,offd=j,k<=n; ++k) { + lb=ls[ns-k]; + if(lb!=ld[nd-k]) goto mismatch; + for(;lb;--lb) + if(tolower(ms[--i]) != tolower(md[--j])) goto mismatch; + --i; --j; + } + mismatch: + + if(os) *os=offs; + if(od) *od=offd; + return k-1; +} + +/* compress the domain name in in and put the result (of maximum length of rhnlen(in)) and + * fill cb with compression information for further strings.*cb may be NULL initially. + * offs is the offset the generated string will be placed in the packet. + * retval: 0 - error, otherwise length + * When done, just free() cb (if it is NULL, free will behave correctly). + * It is guaranteed (and insured by assertions) that the output is smaller or equal in + * size to the input. + */ +unsigned int compress_name(unsigned char *in, unsigned char *out, unsigned int offs, dlist *cb) +{ + compel_t *ci; + unsigned int longest=0,lrem=0,coffs=0; + unsigned int rl=0; + unsigned ilen = rhnlen(in); + unsigned short add=1; + + PDNSD_ASSERT(ilen<=DNSNAMEBUFSIZE, "compress_name: name too long"); + + /* part 1: compression */ + for (ci=dlist_first(*cb); ci; ci=dlist_next(ci)) { + unsigned int rv,rem,to; + if ((rv=domain_match(in, ci->s, &rem,&to))>longest) { + /* + * This has some not obvious implications that should be noted: If a + * domain name as saved in the list has been compressed, we only can + * index the non-compressed part. We rely here that the first occurence + * can't be compressed. So we take the first occurence of a given length. + * This works perfectly, but watch it if you change something. + */ + unsigned int newoffs= ci->index + to; + /* Only use if the offset is not too large. */ + if(newoffs<=0x3fff) { + longest=rv; + lrem=rem; + coffs= newoffs; + } + } + } + if (longest>0) { + PDNSD_ASSERT(lrem+2 <= ilen, "compress_name: length increased"); + memcpy(out, in,lrem); + out[lrem]=0xc0|((coffs&0x3f00)>>8); + out[lrem+1]=coffs&0xff; + rl=lrem+2; + add= lrem!=0; + } + else { + memcpy(out,in,ilen); + rl=ilen; + } + + /* part 2: addition to the cache structure */ + if (add) { + if (!(*cb=dlist_grow(*cb,sizeof(compel_t)+ilen))) + return 0; + ci=dlist_last(*cb); + ci->index=offs; + memcpy(ci->s,in,ilen); + } + return rl; +} + +/* Convert a numeric IP address into a domain name representation + (C string) suitable for PTR records. + buf is assumed to be at least DNSNAMEBUFSIZE bytes in size. +*/ +int a2ptrstr(pdnsd_ca *a, int tp, unsigned char *buf) +{ + if(tp==T_A) { + unsigned char *p=(unsigned char *)&a->ipv4.s_addr; + int n=snprintf(charp buf,DNSNAMEBUFSIZE,"%u.%u.%u.%u.in-addr.arpa.",p[3],p[2],p[1],p[0]); + if(n<0 || n>=DNSNAMEBUFSIZE) + return 0; + } + else +#if ALLOW_LOCAL_AAAA + if(tp==T_AAAA) { + unsigned char *p=(unsigned char *)&a->ipv6; + int i,offs=0; + for (i=15;i>=0;--i) { + unsigned char bt=p[i]; + int n=snprintf(charp(buf+offs), DNSNAMEBUFSIZE-offs,"%x.%x.",bt&0xf,(bt>>4)&0xf); + if(n<0) return 0; + offs+=n; + if(offs>=DNSNAMEBUFSIZE) return 0; + } + if(!strncp(charp(buf+offs),"ip6.arpa.",DNSNAMEBUFSIZE-offs)) + return 0; + } + else +#endif + return 0; + return 1; +} + +/* + * Add records for a host as read from a hosts-style file. + * Returns 1 on success, 0 in an out of memory condition, and -1 when there was a problem with + * the record data. + */ +static int add_host(unsigned char *pn, unsigned char *rns, pdnsd_ca *a, int tp, int a_sz, time_t ttl, unsigned flags, int reverse) +{ + dns_cent_t ce; + + if (!init_cent(&ce, pn, 0, 0, flags DBG0)) + return 0; + if (!add_cent_rr(&ce,tp,ttl,0,CF_LOCAL,a_sz,a DBG0)) + goto free_cent_return0; + if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0)) + goto free_cent_return0; + add_cache(&ce); + free_cent(&ce DBG0); + if (reverse) { + unsigned char b2[DNSNAMEBUFSIZE],rhn[DNSNAMEBUFSIZE]; + if(!a2ptrstr(a,tp,b2)) + return -1; + if (!str2rhn(b2,rhn)) + return -1; + if (!init_cent(&ce, rhn, 0, 0, flags DBG0)) + return 0; + if (!add_cent_rr(&ce,T_PTR,ttl,0,CF_LOCAL,rhnlen(pn),pn DBG0)) + goto free_cent_return0; + if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0)) + goto free_cent_return0; + add_cache(&ce); + free_cent(&ce DBG0); + } + return 1; + + free_cent_return0: + free_cent(&ce DBG0); + return 0; +} + +/* + * Read a file in /etc/hosts-format and add generate rrs for it. + * Errors are largely ignored so that we can skip entries we do not understand + * (but others possibly do). + */ +int read_hosts(const char *fn, unsigned char *rns, time_t ttl, unsigned flags, int aliases, char **errstr) +{ + int rv=0; + FILE *f; + char *buf; + size_t buflen=256; + + if (!(f=fopen(fn,"r"))) { + if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL; + return 0; + } + buf=malloc(buflen); + if(!buf) { + *errstr=NULL; + goto fclose_return; + } + while(getline(&buf,&buflen,f)>=0) { + unsigned int len; + unsigned char *p,*pn,*pi; + unsigned char rhn[DNSNAMEBUFSIZE]; + int tp,sz; + pdnsd_ca a; + + p= ucharp strchr(buf,'#'); + if(p) *p=0; + p= ucharp buf; + for(;;) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + ++p; + } + pi=p; + do { + if(!*++p) goto nextline; + } while(!isspace(*p)); + *p=0; + do { + if(!*++p) goto nextline; + } while (isspace(*p)); + pn=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-pn; + if (parsestr2rhn(pn,len,rhn)!=NULL) + continue; + if (inet_aton(charp pi,&a.ipv4)) { + tp=T_A; + sz=sizeof(struct in_addr); + } else { +#if ALLOW_LOCAL_AAAA /* We don't read them otherwise, as the C library may not be able to to that.*/ + if (inet_pton(AF_INET6,charp pi,&a.ipv6)>0) { + tp=T_AAAA; + sz=sizeof(struct in6_addr); + } else +#endif + continue; + } + { + int res=add_host(rhn, rns, &a, tp,sz, ttl, flags, 1); + if(res==0) { + *errstr= NULL; + goto cleanup_return; + } + else if(res<0) + continue; + } + if(aliases) { + for(;;) { + for(;;) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + ++p; + } + pn=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-pn; + if (parsestr2rhn(pn,len,rhn)!=NULL) + break; + if (add_host(rhn, rns, &a, tp,sz, ttl, flags, 0) == 0) { + *errstr= NULL; + goto cleanup_return; + } + } + } + nextline:; + } + if (feof(f)) + rv=1; + else if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL; + cleanup_return: + free(buf); + fclose_return: + fclose(f); + return rv; +} + + +/* Get the name of an RR type given its value. */ +const char *getrrtpname(int tp) +{ + return tp>=T_MIN && tp<=T_MAX? rrnames[tp-T_MIN]: "[unknown]"; +} + +#if DEBUG>0 +/* + * Const decoders for debugging display + */ +static const char *const c_names[C_NUM] = {"IN","CS","CH","HS"}; +static const char *const qt_names[QT_NUM]={"IXFR","AXFR","MAILB","MAILA","*"}; + +const char *get_cname(int id) +{ + if (id>=C_MIN && id<=C_MAX) + return c_names[id-C_MIN]; + if (id==QC_ALL) + return "*"; + return "[unknown]"; +} + +const char *get_tname(int id) +{ + if (id>=T_MIN && id<=T_MAX) + return rrnames[id-T_MIN]; + else if (id>=QT_MIN && id<=QT_MAX) + return qt_names[id-QT_MIN]; + return "[unknown]"; +} + + +#define NRC 17 +static const char *const e_names[NRC]={ + "no error", + "query format error", + "server failed", + "non-existent domain", + "not supported", + "query refused", + "name exists when it should not", + "RR set exists when it should not", + "RR set that should exist does not", + "server not authoritative for zone", + "name not contained in zone", + "11", + "12", + "13", + "14", + "15", + "bad OPT version" +}; + +const char *get_ename(int id) +{ + if (id>=0 && idaa) + p=mempcpy(p, " AA", 3); + if (hdr->tc) + p=mempcpy(p, " TC", 3); + if (hdr->rd) + p=mempcpy(p, " RD", 3); + if (hdr->ra) + p=mempcpy(p, " RA", 3); + if (hdr->z) + p=mempcpy(p, " Z", 2); + if (hdr->ad) + p=mempcpy(p, " AD", 3); + if (hdr->cd) + p=mempcpy(p, " CD", 3); + *p=0; + + return buf; +} + +#endif + + +#if DEBUG>=9 +/* Based on debug code contributed by Kiyo Kelvin Lee. */ + +void debug_dump_dns_msg(void *data, size_t len) +{ + unsigned char *udata = (unsigned char *)data; +# define dmpchksz 16 + char buf[dmpchksz*4+2]; + size_t i, j, k, l; + + DEBUG_MSG("pointer=%p len=%lu\n", udata, (unsigned long)len); + + for (i = 0; i < len; i += dmpchksz) { + char *cp = buf; + k = l = i + dmpchksz; + if(k > len) k = len; + for (j = i; j < k; ++j) { + int n = sprintf(cp, "%02x ", udata[j]); + cp += n; + } + for (; j < l; ++j) { + *cp++ = ' '; + *cp++ = ' '; + *cp++ = ' '; + } + *cp++ = ' '; + for (j = i; j < k; ++j) { + *cp++ = isprint(udata[j]) ? udata[j] : '.'; + } + PDNSD_ASSERT(cp < buf + sizeof(buf), "debug_dump_dns_msg: line buffer overflowed"); + *cp = '\0'; + DEBUG_MSG("%s\n", buf); + } + + if(len >= sizeof(dns_hdr_t)) { + dns_hdr_t *hdr = (dns_hdr_t *)data; + + DEBUG_MSG( + "id=%04x qr=%x opcode=%x aa=%x tc=%x rd=%x " + "ra=%x z=%x ad=%x cd=%x rcode=%x\n", + ntohs(hdr->id), hdr->qr, hdr->opcode, hdr->aa, hdr->tc, hdr->rd, + hdr->ra, hdr->z, hdr->ad, hdr->cd, hdr->rcode); + DEBUG_MSG( + "qdcount=%04x ancount=%04x nscount=%04x arcount=%04x\n", + ntohs(hdr->qdcount), ntohs(hdr->ancount), ntohs(hdr->nscount), ntohs(hdr->arcount)); + } +} +#endif diff --git a/app/src/main/jni/pdnsd/src/dns.h b/app/src/main/jni/pdnsd/src/dns.h new file mode 100644 index 0000000..0f6a4ac --- /dev/null +++ b/app/src/main/jni/pdnsd/src/dns.h @@ -0,0 +1,309 @@ +/* dns.h - Declarations for dns handling and generic dns functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef DNS_H +#define DNS_H + +#include +#include +#include +#include +#include +#include +#include "rr_types.h" +#include "list.h" +#include "ipvers.h" + +#if (TARGET==TARGET_BSD) +# if !defined(__BIG_ENDIAN) +# if defined(BIG_ENDIAN) +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(_BIG_ENDIAN) +# define __BIG_ENDIAN _BIG_ENDIAN +# endif +# endif +# if !defined(__LITTLE_ENDIAN) +# if defined(LITTLE_ENDIAN) +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# elif defined(_LITTLE_ENDIAN) +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# endif +# endif +# if !defined(__BYTE_ORDER) +# if defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# elif defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# endif +# endif +#endif + +/* Deal with byte orders */ +#ifndef __BYTE_ORDER +# if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) +# error Fuzzy endianness system! Both __LITTLE_ENDIAN and __BIG_ENDIAN have been defined! +# endif +# if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) +# error Strange Endianness-less system! Neither __LITTLE_ENDIAN nor __BIG_ENDIAN has been defined! +# endif +# if defined(__LITTLE_ENDIAN) +# define __BYTE_ORDER __LITTLE_ENDIAN +# elif defined(__BIG_ENDIAN) +# define __BYTE_ORDER __BIG_ENDIAN +# endif +#endif + +/* special rr type codes for queries */ +#define QT_MIN 251 +#define QT_IXFR 251 +#define QT_AXFR 252 +#define QT_MAILB 253 +#define QT_MAILA 254 +#define QT_ALL 255 +#define QT_MAX 255 +#define QT_NUM 5 + +/* rr classes */ +#define C_MIN 1 +#define C_IN 1 +#define C_CS 2 +#define C_CH 3 +#define C_HS 4 +#define C_MAX 4 +#define C_NUM 4 + +/* special classes for queries */ +#define QC_ALL 255 + +/* status codes */ +#define RC_OK 0 +#define RC_FORMAT 1 +#define RC_SERVFAIL 2 +#define RC_NAMEERR 3 +#define RC_NOTSUPP 4 +#define RC_REFUSED 5 +#define RC_BADVERS 16 + +/* + * special internal retvals + */ +#define RC_NOTCACHED 0xfffa +#define RC_CACHED 0xfffb +#define RC_STALE 0xfffc +#define RC_TCPREFUSED 0xfffd +#define RC_TRUNC 0xfffe +#define RC_FATALERR 0xffff + +/* query/response */ +#define QR_QUERY 0 +#define QR_RESP 1 + +/*opcodes */ +#define OP_QUERY 0 +#define OP_IQUERY 1 +#define OP_STATUS 2 + +#if 0 +typedef struct { + /* the name is the first field. It has variable length, so it can't be put in the struct */ + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t rdlength; + /* rdata follows */ +} __attribute__((packed)) rr_hdr_t; + +#define sizeof_rr_hdr_t (sizeof rr_hdr_t) +#else + +/* We will not actually use the rr_hdr_t type, only its size: + sizeof(rr_hdr_t) = 2 + 2 + 4 + 2 */ +#define sizeof_rr_hdr_t 10 +#endif + +#define sizeof_opt_pseudo_rr (1+sizeof_rr_hdr_t) + +#if 0 +typedef struct { + /* The server name and maintainer mailbox are the first two fields. It has variable length, */ + /* so they can't be put in the struct */ + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; +} __attribute__((packed)) soa_r_t; + + +typedef struct { +/* char qname[];*/ + uint16_t qtype; + uint16_t qclass; +} __attribute__((packed)) std_query_t; +#endif + + +typedef struct { + uint16_t id; +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int rd:1; + unsigned int tc:1; + unsigned int aa:1; + unsigned int opcode:4; + unsigned int qr:1; + unsigned int rcode:4; + unsigned int cd:1; + unsigned int ad:1; + unsigned int z :1; + unsigned int ra:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned int qr:1; + unsigned int opcode:4; + unsigned int aa:1; + unsigned int tc:1; + unsigned int rd:1; + unsigned int ra:1; + unsigned int z :1; + unsigned int ad:1; + unsigned int cd:1; + unsigned int rcode:4; +#else +# error "Please define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN" +#endif + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} __attribute__((packed)) dns_hdr_t; + + +/* A structure that can also be used for DNS messages over TCP. */ +typedef struct { +#ifndef NO_TCP_QUERIES + uint16_t len; +#endif + dns_hdr_t hdr; +} __attribute__((packed)) dns_msg_t; + +#ifdef NO_TCP_QUERIES +# define dnsmsghdroffset 0 +#else +# define dnsmsghdroffset 2 +#endif + + +/* Structure for storing EDNS (Extension mechanisms for DNS) information. */ +typedef struct { + unsigned short udpsize; + unsigned short rcode; + unsigned short version; + unsigned char do_flg; +} edns_info_t; + + +/* Macros to retrieve or store integer data that is not necessarily aligned. + Also takes care of network to host byte order. + The pointer cp is advanced and should be of type void* or char*. + These are actually adapted versions of the NS_GET16 and NS_GET32 + macros in the arpa/nameser.h include file in the BIND 9 source. +*/ + +#define GETINT16(s,cp) do { \ + register uint16_t t_s; \ + register const unsigned char *t_cp = (const unsigned char *)(cp); \ + t_s = (uint16_t)*t_cp++ << 8; \ + t_s |= (uint16_t)*t_cp++; \ + (s) = t_s; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define GETINT32(l,cp) do { \ + register uint32_t t_l; \ + register const unsigned char *t_cp = (const unsigned char *)(cp); \ + t_l = (uint32_t)*t_cp++ << 24; \ + t_l |= (uint32_t)*t_cp++ << 16; \ + t_l |= (uint32_t)*t_cp++ << 8; \ + t_l |= (uint32_t)*t_cp++; \ + (l) = t_l; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define PUTINT16(s,cp) do { \ + register uint16_t t_s = (uint16_t)(s); \ + register unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp++ = t_s; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define PUTINT32(l,cp) do { \ + register uint32_t t_l = (uint32_t)(l); \ + register unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp++ = t_l; \ + (cp) = (void *)t_cp; \ +} while (0) + + +/* Size (number of bytes) of buffers used to hold domain names. */ +#define DNSNAMEBUFSIZE 256 + +/* Recursion depth. */ +#define MAX_HOPS 20 + +/* + * Types for compression buffers. + */ +typedef struct { + unsigned int index; + unsigned char s[0]; +} compel_t; + + + +int decompress_name(unsigned char *msg, size_t msgsz, unsigned char **src, size_t *sz, unsigned char *tgt, unsigned int *len); +/* int domain_name_match(const unsigned char *ms, const unsigned char *md, int *os, int *od); */ +unsigned int domain_match(const unsigned char *ms, const unsigned char *md, unsigned int *os, unsigned int *od); +unsigned int compress_name(unsigned char *in, unsigned char *out, unsigned int offs, dlist *cb); +int a2ptrstr(pdnsd_ca *a, int tp, unsigned char *buf); +int read_hosts(const char *fn, unsigned char *rns, time_t ttl, unsigned flags, int aliases, char **errstr); + +const char *getrrtpname(int tp); +#if DEBUG>0 +const char *get_cname(int id); +const char *get_tname(int id); +const char *get_ename(int id); +#define DNSFLAGSMAXSTRSIZE (7*3+1) +char *dnsflags2str(dns_hdr_t *hdr, char *buf); +#endif + +#if DEBUG>=9 +void debug_dump_dns_msg(void *data, size_t len); +#define DEBUG_DUMP_DNS_MSG(d,l) {if(debug_p && global.verbosity>=9) debug_dump_dns_msg(d,l);} +#else +#define DEBUG_DUMP_DNS_MSG(d,l) +#endif + +#endif diff --git a/app/src/main/jni/pdnsd/src/dns_answer.c b/app/src/main/jni/pdnsd/src/dns_answer.c new file mode 100644 index 0000000..6a2a5b5 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/dns_answer.c @@ -0,0 +1,2170 @@ +/* dns_answer.c - Receive and process incoming dns queries. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * STANDARD CONFORMITY + * + * There are several standard conformity issues noted in the comments. + * Some additional comments: + * + * I always set RA but I ignore RD largely (in everything but CNAME recursion), + * not because it is not supported, but because I _always_ do a recursive + * resolve in order to be able to cache the results. + */ + +#include +#include "ipvers.h" +#include +#include +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "list.h" +#include "dns.h" +#include "dns_answer.h" +#include "dns_query.h" +#include "helpers.h" +#include "cache.h" +#include "error.h" +#include "debug.h" + + +/* + * This is for error handling to prevent spewing the log files. + * Maximums of different message types are set. + * Races do not really matter here, so no locks. + */ +#define TCP_MAX_ERRS 10 +#define UDP_MAX_ERRS 10 +#define MEM_MAX_ERRS 10 +#define THRD_MAX_ERRS 10 +#define MISC_MAX_ERRS 10 +static volatile unsigned long da_tcp_errs=0; +static volatile unsigned long da_udp_errs=0; +static volatile unsigned long da_mem_errs=0; +static volatile unsigned long da_thrd_errs=0; +#if DEBUG>0 +static volatile unsigned long da_misc_errs=0; +#endif +static volatile int procs=0; /* active query processes */ +static volatile int qprocs=0; /* queued query processes */ +static volatile unsigned long dropped=0,spawned=0; +static volatile unsigned thrid_cnt=0; +static pthread_mutex_t proc_lock = PTHREAD_MUTEX_INITIALIZER; + +#ifdef SOCKET_LOCKING +static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +typedef union { +#ifdef ENABLE_IPV4 +# if (TARGET==TARGET_LINUX) + struct in_pktinfo pi4; +# else + struct in_addr ai4; +# endif +#endif +#ifdef ENABLE_IPV6 + struct in6_pktinfo pi6; +#endif +} pkt_info_t; + + +typedef struct { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } addr; + + pkt_info_t pi; + + int sock; + int proto; + size_t len; + unsigned char buf[0]; /* Actual size determined by global.udpbufsize */ +} udp_buf_t; + + +/* ALLOCINITIALSIZE should be at least sizeof(dns_msg_t) = 2+12 */ +#define ALLOCINITIALSIZE 256 +/* This mask corresponds to a chunk size of 128 bytes. */ +#define ALLOCCHUNKSIZEMASK ((size_t)0x7f) + +typedef struct { + unsigned short qtype; + unsigned short qclass; + unsigned char query[0]; +} dns_queryel_t; + + +#define S_ANSWER 1 +#define S_AUTHORITY 2 +#define S_ADDITIONAL 3 + +typedef struct { + unsigned short tp,dlen; + unsigned char nm[0]; + /* unsigned char data[0]; */ +} sva_t; + + +/* + * Mark an additional record as added to avoid double records. + */ +static int sva_add(dlist *sva, const unsigned char *rhn, unsigned short tp, unsigned short dlen, void* data) +{ + if (sva) { + size_t rlen=rhnlen(rhn); + sva_t *st; + if (!(*sva=dlist_grow(*sva,sizeof(sva_t)+rlen+dlen))) { + return 0; + } + st=dlist_last(*sva); + st->tp=tp; + st->dlen=dlen; + memcpy(mempcpy(st->nm,rhn,rlen),data,dlen); + } + return 1; +} + +/* ans_ttl computes the ttl value to return to the client. + This is the ttl value stored in the cache entry minus the time + the cache entry has lived in the cache. + Local cache entries are an exception, they never "age". +*/ +inline static time_t ans_ttl(rr_set_t *rrset, time_t queryts) +{ + time_t ttl= rrset->ttl; + + if (!(rrset->flags&CF_LOCAL)) { + time_t tpassed= queryts - rrset->ts; + if(tpassed<0) tpassed=0; + ttl -= tpassed; + if(ttl<0) ttl=0; + } + return ttl; +} + +/* follow_cname_chain takes a cache entry and a buffer (must be at least DNSNAMEBUFSIZE bytes), + and copies the name indicated by the first cname record in the cache entry. + The name is returned in length-byte string notation. + follow_cname_chain returns 1 if a cname record is found, otherwise 0. +*/ +inline static int follow_cname_chain(dns_cent_t *c, unsigned char *name) +{ + rr_set_t *rrset=getrrset_CNAME(c); + rr_bucket_t *rr; + if (!rrset || !(rr=rrset->rrs)) + return 0; + PDNSD_ASSERT(rr->rdlen <= DNSNAMEBUFSIZE, "follow_cname_chain: record too long"); + memcpy(name,rr->data,rr->rdlen); + return 1; +} + + +/* + * Add data from a rr_bucket_t (as in cache) into a dns message in ans. Ans is grown + * to fit, sz is the old size of the packet (it is modified so at the end of the procedure + * it is the new size), type is the rr type and ltime is the time in seconds the record is + * old. + * cb is the buffer used for message compression. *cb should be NULL when you call compress_name + * or add_to_response the first time. + * It gets filled with a pointer to compression information that can be reused in subsequent calls + * to add_to_response. + * sect is the section (S_ANSWER, S_AUTHORITY or S_ADDITIONAL) in which the record + * belongs logically. Note that you still have to add the rrs in the right order (answer rrs first, + * then authority and last additional). + */ +static int add_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned short type, uint32_t ttl, + unsigned int dlen, void *data, char section, unsigned *udp, dlist *cb) +{ + size_t osz= *sz; + unsigned int ilen,blen,rdlen; + unsigned char *rrht; + + { + unsigned int nlen; + unsigned char nbuf[DNSNAMEBUFSIZE]; + + if (!(nlen=compress_name(rrn,nbuf,*sz,cb))) + return 0; + + /* This buffer is usually over-allocated due to compression. + Never mind, just a few bytes, and the buffer is freed soon. */ + { + size_t newsz= dnsmsghdroffset + *sz + nlen + sizeof_rr_hdr_t + dlen; + if(newsz > *allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(*ans,newallocsz); + if (!newans) + return 0; + *ans=newans; + *allocsz=newallocsz; + } + } + memcpy((unsigned char *)(&(*ans)->hdr)+ *sz, nbuf, nlen); + *sz += nlen; + } + + /* the rr header will be filled in later. Just reserve some space for it. */ + rrht= ((unsigned char *)(&(*ans)->hdr)) + *sz; + *sz += sizeof_rr_hdr_t; + + switch (type) { + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + PDNSD_ASSERT(rdlen <= dlen, "T_CNAME/T_MB/...: got longer"); + *sz+=rdlen; + break; +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + *sz+=rdlen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_MINFO/T_RP: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + PDNSD_ASSERT(rdlen <= dlen, "T_MINFO/T_RP: got longer"); + *sz+=blen; + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + PDNSD_ASSERT(dlen > 2, "T_MX/T_AFSDB/...: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,2); + *sz+=2; + if (!(blen=compress_name(((unsigned char *)data)+2, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=2+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_MX/T_AFSDB/...: got longer"); + *sz+=blen; + break; + case T_SOA: + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + *sz+=rdlen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_SOA: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + *sz+=blen; + ilen+=rhnlen(((unsigned char *)data)+ilen); + PDNSD_ASSERT(rdlen <= ilen, "T_SOA: got longer"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data)+ilen,20); + rdlen+=20; + PDNSD_ASSERT(rdlen <= dlen, "T_SOA: rr botch"); + *sz+=20; + break; +#if IS_CACHED_PX + case T_PX: + PDNSD_ASSERT(dlen > 2, "T_PX: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,2); + *sz+=2; + ilen=2; + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=2+blen; + *sz+=blen; + ilen+=rhnlen(((unsigned char *)data)+ilen); + PDNSD_ASSERT(rdlen <= ilen, "T_PX: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + PDNSD_ASSERT(rdlen <= dlen, "T_PX: got longer"); + *sz+=blen; + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + PDNSD_ASSERT(dlen > 6, "T_SRV: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,6); + *sz+=6; + if (!(blen=compress_name(((unsigned char *)data)+6, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=6+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_SRV: got longer"); + *sz+=blen; + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + if (!(blen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=blen; + *sz+=blen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_NXT: got longer"); + PDNSD_ASSERT(dlen >= ilen, "T_NXT: rr botch"); + if (dlen > ilen) { + unsigned int wlen = dlen - ilen; + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data)+ilen,wlen); + *sz+=wlen; + rdlen+=wlen; + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + PDNSD_ASSERT(dlen > 4, "T_NAPTR: rr botch"); + ilen=4; + { + int j; + for (j=0;j<3;j++) { + ilen += ((unsigned)*(((unsigned char *)data)+ilen)) + 1; + PDNSD_ASSERT(dlen > ilen, "T_NAPTR: rr botch 2"); + } + } + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data),ilen); + (*sz)+=ilen; + + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=ilen+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_NAPTR: got longer"); + *sz+=blen; + break; +#endif + default: + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data),dlen); + rdlen=dlen; + *sz+=dlen; + } + + if (udp && *sz>*udp && section==S_ADDITIONAL) /* only add the record if we do not increase the length over 512 */ + *sz=osz; /* (or possibly more if the request used EDNS) in additionals for udp answer. */ + else { + PUTINT16(type,rrht); + PUTINT16(C_IN,rrht); + PUTINT32(ttl,rrht); + PUTINT16(rdlen,rrht); + + switch (section) { + case S_ANSWER: + (*ans)->hdr.ancount=htons(ntohs((*ans)->hdr.ancount)+1); + break; + case S_AUTHORITY: + (*ans)->hdr.nscount=htons(ntohs((*ans)->hdr.nscount)+1); + break; + case S_ADDITIONAL: + (*ans)->hdr.arcount=htons(ntohs((*ans)->hdr.arcount)+1); + break; + } + } + + return 1; +} + +/* Add an OPT pseudo RR containing EDNS info. + Can only be added to the additional section! +*/ +int add_opt_pseudo_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned short udpsize, unsigned short rcode, + unsigned short ednsver, unsigned short Zflags) +{ + unsigned char *ptr; + size_t newsz= dnsmsghdroffset + *sz + sizeof_opt_pseudo_rr; + if(newsz > *allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(*ans,newallocsz); + if (!newans) + return 0; + *ans=newans; + *allocsz=newallocsz; + } + + ptr= ((unsigned char *)(&(*ans)->hdr)) + *sz; + *ptr++ = 0; /* Empty name */ + PUTINT16(T_OPT,ptr); /* type field */ + PUTINT16(udpsize,ptr); /* class field */ + *ptr++ = rcode>>4; /* 4 byte TTL field */ + *ptr++ = ednsver; + PUTINT16(Zflags,ptr); + PUTINT16(0,ptr); /* rdlen field */ + /* Empty RDATA. */ + + *sz += sizeof_opt_pseudo_rr; + /* Increment arcount field in dns header. */ + (*ans)->hdr.arcount = htons(ntohs((*ans)->hdr.arcount)+1); + return 1; +} + +/* Remove the last entry in the additional section, + assuming it is an OPT pseudo RR of fixed size. + Returns the new message size if successful, or + zero if an inconsistency is detected. +*/ +size_t remove_opt_pseudo_rr(dns_msg_t *ans, size_t sz) +{ + uint16_t acnt=ntohs(ans->hdr.arcount), type; + unsigned char *ptr; + /* First do some sanity checks. */ + if(!(acnt>0 && sz >= sizeof(dns_hdr_t)+sizeof_opt_pseudo_rr)) + return 0; + sz -= sizeof_opt_pseudo_rr; + ptr= ((unsigned char *)(&ans->hdr)) + sz; + if(*ptr++) + return 0; /* Name must be empty. */ + GETINT16(type,ptr); + if(type!=T_OPT) + return 0; /* RR type must be OPT. */ + /* Decrement arcount field in dns header. */ + ans->hdr.arcount = htons(acnt-1); + return sz; +} + +typedef struct rre_s { + unsigned short tp; + unsigned short tsz; /* Size of tnm field */ + uint32_t ttl; /* ttl of the record in the answer (if tp==T_NS or T_SOA) */ + unsigned char tnm[0]; /* Name for the domain a record refers to */ + /* unsigned char nm[0]; */ /* Name of the domain the record is for (if tp==T_NS or T_SOA) */ +} rr_ext_t; + + +/* types for the tp field */ +/* #define RRETP_NS T_NS */ /* For name server: add to authority, add address to additional. */ +/* #define RRETP_SOA T_SOA */ /* For SOA record: add to authority. */ +#define RRETP_ADD 0 /* For other records: add the address of buf to additional */ + +static int add_ar(dlist *ar,unsigned short tp, unsigned short tsz,void *tnm,unsigned char *nm, uint32_t ttl) +{ + rr_ext_t *re; + unsigned char *p; + size_t nmsz=0,size=sizeof(rr_ext_t)+tsz; + if(tp==T_NS || tp==T_SOA) { + nmsz=rhnlen(nm); + size += nmsz; + } + if (!(*ar=dlist_grow(*ar,size))) + return 0; + re=dlist_last(*ar); + re->tp=tp; + re->tsz=tsz; + re->ttl=ttl; + p=mempcpy(re->tnm,tnm,tsz); + if(tp==T_NS || tp==T_SOA) { + memcpy(p,nm,nmsz); + } + return 1; +} + + +/* Select a random rr record from a list. */ +inline static rr_bucket_t *randrr(rr_bucket_t *rrb) +{ + rr_bucket_t *rr; + unsigned cnt=0; + + /* In order to have an equal chance for each record to be selected, we have to count first. */ + for(rr=rrb; rr; rr=rr->next) ++cnt; + + /* We do not use the pdnsd random functions (these might use /dev/urandom if the user is paranoid, + * and we do not need any good PRNG here). */ + if(cnt) for(cnt=random()%cnt; cnt; --cnt) rrb=rrb->next; + + return rrb; +} + +#if IS_CACHED_SRV +#define AR_NUM 6 +#else +#define AR_NUM 5 +#endif +static const int ar_recs[AR_NUM]={T_NS, T_MD, T_MF, T_MB, T_MX +#if IS_CACHED_SRV + ,T_SRV +#endif +}; +/* offsets from record data start to server name */ +static const int ar_offs[AR_NUM]={0,0,0,0,2 +#if IS_CACHED_SRV + ,6 +#endif +}; + +/* This adds an rrset, optionally randomizing the first element it adds. + * if that is done, all rrs after the randomized one appear in order, starting from + * that one and wrapping over if needed. */ +static int add_rrset(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned tp, time_t queryts, + dns_cent_t *cached, unsigned *udp, dlist *cb, dlist *sva, dlist *ar) +{ + rr_set_t *crrset=getrrset(cached,tp); + + if (crrset && crrset->rrs) { + rr_bucket_t *b; + rr_bucket_t *first=NULL; /* Initialized to inhibit compiler warning */ + int i; + short rnd_recs=global.rnd_recs; + + b=crrset->rrs; + if (rnd_recs) b=first=randrr(crrset->rrs); + + while (b) { + if (!add_rr(ans, sz, allocsz, rrn, tp, ans_ttl(crrset,queryts), + b->rdlen, b->data, S_ANSWER, udp, cb)) + return 0; + if (tp==T_NS || tp==T_A || tp==T_AAAA) { + /* mark it as added */ + if (!sva_add(sva,rrn,tp,b->rdlen,b->data)) + return 0; + } + /* Mark for additional address records. XXX: this should be a more effective algorithm; at least the list is small */ + for (i=0;irdlen-ar_offs[i],((unsigned char *)(b->data))+ar_offs[i], + ucharp "", 0)) + return 0; + break; + } + } + b=b->next; + if (rnd_recs) { + if(!b) b=crrset->rrs; /* wraparound */ + if(b==first) break; + } + } + } + return 1; +} + +/* + * Add the fitting elements of the cached record to the message in ans, where ans + * is grown to fit, sz is the size of the packet and is modified to be the new size. + * The query is in qe. + * cb is the buffer used for message compression. *cb should be NULL if you call add_to_response + * the first time. It gets filled with a pointer to compression information that can be + * reused in subsequent calls to add_to_response. + */ +static int add_to_response(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned qtype, time_t queryts, + dns_cent_t *cached, unsigned *udp, dlist *cb, dlist *sva, dlist *ar) +{ + /* First of all, unless we have records of qtype, add cnames. + Well, actually, there should be at max one cname. */ + if (qtype!=T_CNAME && qtype!=QT_ALL && !(qtype>=T_MIN && qtype<=T_MAX && have_rr(cached,qtype))) + if (!add_rrset(ans, sz, allocsz, rrn, T_CNAME, queryts, cached, udp, cb, sva, ar)) + return 0; + + /* We need no switch for qclass, since we already have filtered packets we cannot understand */ + if (qtype==QT_AXFR || qtype==QT_IXFR) { + /* I do not know what to do in this case. Since we do not maintain zones (and since we are + no master server, so it is not our task), I just return an error message. If anyone + knows how to do this better, please notify me. + Anyway, this feature is rarely used in client communication, and there is no need for + other name servers to ask pdnsd. Btw: many bind servers reject an ?XFR query for security + reasons. */ + return 0; + } else if (qtype==QT_MAILB) { + if (!add_rrset(ans, sz, allocsz, rrn, T_MB, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MG, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MR, queryts, cached, udp, cb, sva, ar)) + return 0; + } else if (qtype==QT_MAILA) { + if (!add_rrset(ans, sz, allocsz, rrn, T_MD, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MF, queryts, cached, udp, cb, sva, ar)) + return 0; + } else if (qtype==QT_ALL) { + int i, n= NRRITERLIST(cached); + const unsigned short *iterlist= RRITERLIST(cached); + for (i=0; i=T_MIN && qtype<=T_MAX) { + if (!add_rrset(ans, sz, allocsz, rrn, qtype, queryts, cached, udp, cb, sva, ar)) + return 0; + } else /* Shouldn't get here. */ + return 0; +#if 0 + if (!ntohs((*ans)->hdr.ancount)) { + /* Add a SOA if we have one and no other records are present in the answer. + * This is to aid caches so that they have a ttl. */ + if (!add_rrset(ans, sz, allocsz, rrn, T_SOA , queryts, cached, udp, cb, sva, ar)) + return 0; + } +#endif + return 1; +} + +/* + * Add an additional + */ +static int add_additional_rr(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, unsigned tp, time_t ttl, + unsigned dlen, void *data, int sect, unsigned *udp, dlist *cb, dlist *sva) +{ + sva_t *st; + + /* Check if already added; no double additionals */ + for (st=dlist_first(*sva); st; st=dlist_next(st)) { + if (st->tp==tp && rhnicmp(st->nm,rhn) && st->dlen==dlen && + (memcmp(skiprhn(st->nm),data, dlen)==0)) + { + return 1; + } + } + /* add_rr will do nothing when udp!=NULL and sz>*udp. */ + if(!add_rr(ans, rlen, allocsz, rhn, tp, ttl, dlen, data, sect, udp, cb)) + return 0; + /* mark it as added */ + if (!sva_add(sva,rhn,tp,dlen,data)) + return 0; + + return 1; +} + +/* + * Add one or more additionals from an rr bucket. + */ +static int add_additional_rrs(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, unsigned tp, time_t ttl, + rr_bucket_t *rrb, int sect, unsigned *udp, dlist *cb, dlist *sva) +{ + rr_bucket_t *rr; + rr_bucket_t *first=NULL; /* Initialized to inhibit compiler warning */ + short rnd_recs=global.rnd_recs; + + rr=rrb; + if (rnd_recs) rr=first=randrr(rrb); + + while(rr) { + if (!add_additional_rr(ans, rlen, allocsz, rhn, tp, ttl, rr->rdlen,rr->data, sect, udp, cb, sva)) + return 0; + rr=rr->next; + if (rnd_recs) { + if(!rr) rr=rrb; /* wraparound */ + if(rr==first) break; + } + } + return 1; +} + +/* + * The code below actually handles A and AAAA additionals. + */ +static int add_additional_a(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, time_t queryts, + unsigned *udp, dlist *cb, dlist *sva) +{ + dns_cent_t *ae; + int retval = 1; + + if ((ae=lookup_cache(rhn,NULL))) { + rr_set_t *rrset; rr_bucket_t *rr; + rrset=getrrset_A(ae); + if (rrset && (rr=rrset->rrs)) + if (!add_additional_rrs(ans, rlen, allocsz, + rhn, T_A, ans_ttl(rrset,queryts), + rr, S_ADDITIONAL, udp, cb, sva)) + retval = 0; + +#if IS_CACHED_AAAA + if(retval) { + rrset=getrrset_AAAA(ae); + if (rrset && (rr=rrset->rrs)) + if (!add_additional_rrs(ans, rlen, allocsz, + rhn, T_AAAA, ans_ttl(rrset,queryts), + rr, S_ADDITIONAL, udp, cb, sva)) + retval = 0; + } +#endif + free_cent(ae DBG1); + pdnsd_free(ae); + } + return retval; +} + +/* + * Compose an answer message for the decoded query in ql, hdr is the header of the dns request + * rlen is set to be the answer length. + * If udp is not NULL, *udp indicates the max length the dns response may have. + */ +static dns_msg_t *compose_answer(llist *ql, dns_hdr_t *hdr, size_t *rlen, edns_info_t *ednsinfo, unsigned *udp, int *rcodep) +{ + unsigned short rcode=RC_OK, aa=1; + dlist cb=NULL; + dlist sva=NULL; + dlist ar=NULL; + time_t queryts=time(NULL); + dns_queryel_t *qe; + dns_msg_t *ans; + size_t allocsz= ALLOCINITIALSIZE; + dns_cent_t *cached; + + ans=(dns_msg_t *)pdnsd_malloc(allocsz); + if (!ans) + goto return_ans; + ans->hdr.id=hdr->id; + ans->hdr.qr=QR_RESP; + ans->hdr.opcode=OP_QUERY; + ans->hdr.aa=0; + ans->hdr.tc=0; /* If tc is needed, it is set when the response is sent in udp_answer_thread. */ + ans->hdr.rd=hdr->rd; + ans->hdr.ra=1; + ans->hdr.z=0; + ans->hdr.ad=0; + ans->hdr.cd=0; + ans->hdr.rcode=rcode; + ans->hdr.qdcount=0; /* this is first filled in and will be modified */ + ans->hdr.ancount=0; + ans->hdr.nscount=0; + ans->hdr.arcount=0; + + *rlen=sizeof(dns_hdr_t); + /* first, add the query to the response */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + unsigned int qclen; + size_t newsz= dnsmsghdroffset + *rlen + rhnlen(qe->query) + 4; + if(newsz > allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(ans,newallocsz); + if (!newans) + goto error_ans; + ans=newans; + allocsz=newallocsz; + } + + { + unsigned char *p = ((unsigned char *)&ans->hdr) + *rlen; + /* the first name occurrence will not be compressed, + but the offset needs to be stored for future compressions */ + if (!(qclen=compress_name(qe->query,p,*rlen,&cb))) + goto error_ans; + p += qclen; + PUTINT16(qe->qtype,p); + PUTINT16(qe->qclass,p); + } + *rlen += qclen+4; + ans->hdr.qdcount=htons(ntohs(ans->hdr.qdcount)+1); + } + + /* Barf if we get a query we cannot answer */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + if ((PDNSD_NOT_CACHED_TYPE(qe->qtype) && + (qe->qtype!=QT_MAILB && qe->qtype!=QT_MAILA && qe->qtype!=QT_ALL)) || + (qe->qclass!=C_IN && qe->qclass!=QC_ALL)) + { + DEBUG_MSG("Unsupported QTYPE or QCLASS.\n"); + ans->hdr.rcode=rcode=RC_NOTSUPP; + goto cleanup_return; + } + } + + /* second, the answer section */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + int hops; + unsigned char qname[DNSNAMEBUFSIZE]; + + rhncpy(qname,qe->query); + /* look if we have a cached copy. otherwise, perform a nameserver query. Same with timeout */ + hops=MAX_HOPS; + do { + int rc; + unsigned char c_soa=cundef; + if ((rc=dns_cached_resolve(qname,qe->qtype, &cached, MAX_HOPS,queryts,&c_soa))!=RC_OK) { + ans->hdr.rcode=rcode=rc; + if(rc==RC_NAMEERR) { + if(c_soa!=cundef) { + /* Try to add a SOA record to the authority section. */ + unsigned scnt=rhnsegcnt(qname); + if(c_soaflags&CF_NEGATIVE)) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + if (!add_rr(&ans,rlen,&allocsz,cached->qname,T_SOA,ans_ttl(rrset,queryts), + rr->rdlen,rr->data,S_AUTHORITY,udp,&cb)) + goto error_cached; + } + } + free_cent(cached DBG1); + pdnsd_free(cached); + } + } + + /* Possibly add an OPT pseudo-RR to the additional section. */ + if(ednsinfo) { + if(!add_opt_pseudo_rr(&ans, rlen, &allocsz, global.udpbufsize, rcode, 0,0)) + goto error_ans; + } + } + goto cleanup_return; + } + if(!(cached->flags&DF_LOCAL)) + aa=0; + + if (!add_to_response(&ans,rlen,&allocsz,qname,qe->qtype,queryts,cached,udp,&cb,&sva,&ar)) + goto error_cached; + if (hdr->rd && qe->qtype!=T_CNAME && qe->qtype!=QT_ALL && + !(qe->qtype>=T_MIN && qe->qtype<=T_MAX && have_rr(cached,qe->qtype)) && + follow_cname_chain(cached,qname)) + /* The rd bit is set and the response does not contain records of the requested type, + * but the response does contain a cname, so repeat the inquiry with the cname. + * add_to_response() has already added the cname to the response. + * Because of follow_cname_chain(), qname now contains the last cname in the chain. */ + ; + else { + /* maintain a list (ar) for authority records: We will add every name server that was + listed as authoritative in a reply we received (and only those) to this list. + This list will be used to fill the authority and additional sections of our own reply. + We only do this for the last record in a cname chain, to prevent answer bloat. */ + rr_set_t *rrset; + int rretp=T_NS; + if((qe->qtype>=T_MIN && qe->qtype<=T_MAX && !have_rr(cached,qe->qtype)) || + (qe->qtype==QT_MAILB && !have_rr_MB(cached) && !have_rr_MG(cached) && !have_rr_MR(cached)) || + (qe->qtype==QT_MAILA && !have_rr_MD(cached) && !have_rr_MF(cached))) + { + /* no record of requested type in the answer section. */ + rretp=T_SOA; + } + rrset=getrrset(cached,rretp); + if(rrset && (rrset->flags&CF_NEGATIVE)) + rrset=NULL; + if(!rrset) { + /* Try to find a name server higher up the hierarchy . + */ + dns_cent_t *prev=cached; + unsigned scnt=rhnsegcnt(prev->qname); + unsigned tcnt=(rretp==T_NS?prev->c_ns:prev->c_soa); + if((cached=lookup_cache((tcnt!=cundef && tcntqname,scnt-tcnt):prev->qname,NULL))) { + rrset=getrrset(cached,rretp); + if(rrset && (rrset->flags&CF_NEGATIVE)) + rrset=NULL; + } + if(!rrset && (prev->flags&DF_LOCAL)) { + unsigned char *nm=getlocalowner(prev->qname,rretp); + if(nm) { + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + if((cached=lookup_cache(nm,NULL))) + rrset=getrrset(cached,rretp); + } + } + free_cent(prev DBG1); + pdnsd_free(prev); + } + if (rrset) { + rr_bucket_t *rr; + for (rr=rrset->rrs; rr; rr=rr->next) { + if (!add_ar(&ar, rretp, rr->rdlen,rr->data, cached->qname, + ans_ttl(rrset,queryts))) + goto error_cached; + } + } + hops=0; /* this will break the loop */ + } + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + } while (--hops>=0); + } + + { + rr_ext_t *rre; + /* Add the authority section */ + for (rre=dlist_first(ar); rre; rre=dlist_next(rre)) { + if (rre->tp == T_NS || rre->tp == T_SOA) { + unsigned char *nm = rre->tnm + rre->tsz; + if (!add_additional_rr(&ans, rlen, &allocsz, + nm, rre->tp, rre->ttl, rre->tsz, rre->tnm, + S_AUTHORITY, udp, &cb, &sva)) + { + goto error_ans; + } + } + } + + /* Add the additional section, but only if we stay within the UDP buffer limit. */ + /* If a pseudo RR doesn't fit, nothing else will. */ + if(!(udp && *rlen+sizeof_opt_pseudo_rr>*udp)) { + + /* Possibly add an OPT pseudo-RR to the additional section. */ + if(ednsinfo) { + if(!add_opt_pseudo_rr(&ans, rlen, &allocsz, global.udpbufsize, rcode, 0,0)) + goto error_ans; + } + + /* now add the name server addresses */ + for (rre=dlist_first(ar); rre; rre=dlist_next(rre)) { + if (rre->tp == T_NS || rre->tp == RRETP_ADD) { + if (!add_additional_a(&ans, rlen, &allocsz, + rre->tnm, queryts, udp, &cb, &sva)) + goto error_ans; + } + } + } + } + if (aa) + ans->hdr.aa=1; + goto cleanup_return; + + /* You may not like goto's, but here we avoid lots of code duplication. */ +error_cached: + free_cent(cached DBG1); + pdnsd_free(cached); +error_ans: + pdnsd_free(ans); + ans=NULL; +cleanup_return: + dlist_free(ar); + dlist_free(sva); + dlist_free(cb); +return_ans: + if(rcodep) *rcodep=rcode; + return ans; +} + +/* + * Decode the query (the query messgage is in data and rlen bytes long) into a dlist. + * XXX: data needs to be aligned. + * The return value can be RC_OK or RC_TRUNC, in which case the (partially) constructed list is + * returned in qp, or something else (RC_FORMAT or RC_SERVFAIL), in which case no list is returned. + * + * *ptrrem will be assigned the address just after the questions sections in the message, and *lenrem + * the remaining message length after the questions section. These values are only meaningful if the + * return value is RC_OK. + */ +static int decode_query(unsigned char *data, size_t rlen, unsigned char **ptrrem, size_t *lenrem, llist *qp) +{ + int i,res=RC_OK; + dns_hdr_t *hdr=(dns_hdr_t *)data; /* aligned, so no prob. */ + unsigned char *ptr=(unsigned char *)(hdr+1); + size_t sz= rlen - sizeof(dns_hdr_t); + uint16_t qdcount=ntohs(hdr->qdcount); + + llist_init(qp); + for (i=0; iqtype,ptr); + GETINT16(qe->qclass,ptr); + sz-=4; + memcpy(qe->query,qbuf,qlen); + } + + if(ptrrem) *ptrrem=ptr; + if(lenrem) *lenrem=sz; + return res; +} + + +/* Scan the additional section of a query message for an OPT pseudo RR. + data and rlen are as in decode_query(). Note in particular that data needs to be aligned! + ptr should point the beginning of the additional section, sz should contain the + length of this remaining part of the message and numrr the number of resource records in the section. + *numopt is incremented with the number of OPT RRs found (should be at most one). + + Note that a return value of RC_OK means the additional section was parsed without errors, not that + an OPT pseudo RR was found! Check the value of *numopt for the latter. + + The structure pointed to by ep is filled with the information of the first OPT pseudo RR found, + but only if *numopt was set to zero before the call. +*/ +static int decode_query_additional(unsigned char *data, size_t rlen, unsigned char *ptr, size_t sz, int numrr, + int *numopt, edns_info_t *ep) +{ + int i, res; + + for (i=0; i0 + if(nmbuf[0]!=0) { + DEBUG_MSG("decode_query_additional: name in OPT record not empty!\n"); + } +#endif + ep->udpsize= class; + ep->rcode= ((uint16_t)ttlp[0]<<4) | ((dns_hdr_t *)data)->rcode; + ep->version= ttlp[1]; + ep->do_flg= (ttlp[2]>>7)&1; +#if DEBUG>0 + if(debug_p) { + unsigned int Zflags= ((uint16_t)ttlp[2]<<8) | ttlp[3]; + if(Zflags & 0x7fff) { + DEBUG_MSG("decode_query_additional: Z field contains unknown nonzero bits (%04x).\n", + Zflags); + } + if(rdlen) { + DEBUG_MSG("decode_query_additional: RDATA field in OPT record not empty!\n"); + } + } +#endif + } + else { + DEBUG_MSG("decode_query_additional: ingnoring surplus OPT record.\n"); + } + } + else { + DEBUG_MSG("decode_query_additional: ignoring record of type %s (%d).\n", + getrrtpname(type), type); + } + + /* Skip RDATA field. */ + sz -= rdlen; + ptr += rdlen; + } + + return RC_OK; +} + +/* Make a dns error reply message + * Id is the query id and still in network order. + * op is the opcode to fill in, rescode - name says it all. + */ +static void mk_error_reply(unsigned short id, unsigned short opcode,unsigned short rescode,dns_hdr_t *rep) +{ + rep->id=id; + rep->qr=QR_RESP; + rep->opcode=opcode; + rep->aa=0; + rep->tc=0; + rep->rd=0; + rep->ra=1; + rep->z=0; + rep->ad=0; + rep->cd=0; + rep->rcode=rescode; + rep->qdcount=0; + rep->ancount=0; + rep->nscount=0; + rep->arcount=0; +} + +/* + * Analyze and answer the query in data. The answer is returned. rlen is at call the query length and at + * return the length of the answer. You have to free the answer after sending it. + */ +static dns_msg_t *process_query(unsigned char *data, size_t *rlenp, unsigned *udp, int *rcodep) +{ + size_t rlen= *rlenp; + int res; + dns_hdr_t *hdr; + llist ql; + dns_msg_t *ans; + edns_info_t ednsinfo= {0}, *ednsinfop= NULL; + + DEBUG_MSG("Received query (msg len=%u).\n", (unsigned int)rlen); + DEBUG_DUMP_DNS_MSG(data, rlen); + + /* + * We will ignore all records that come with a query, except for the actual query records, + * and possible OPT pseudo RRs in the addtional section. + * We will send back the query in the response. We will reject all non-queries, and + * some not supported thingies. + * If anyone notices behaviour that is not in standard conformance, please notify me! + */ + hdr=(dns_hdr_t *)data; + if (rlen<2) { + DEBUG_MSG("Message too short.\n"); + return NULL; /* message too short: no id provided. */ + } + if (rlenqr!=QR_QUERY) { + DEBUG_MSG("The QR bit indicates this is a response, not a query.\n"); + return NULL; /* RFC says: discard */ + } + if (hdr->opcode!=OP_QUERY) { + DEBUG_MSG("Not a standard query (opcode=%u).\n",hdr->opcode); + res=RC_NOTSUPP; + goto error_reply; + } +#if DEBUG>0 + if(debug_p) { + char flgsbuf[DNSFLAGSMAXSTRSIZE]; + dnsflags2str(hdr, flgsbuf); + if(flgsbuf[0]) { + DEBUG_MSG("Flags:%s\n", flgsbuf); + } + } +#endif + if (hdr->z!=0) { + DEBUG_MSG("Malformed query (nonzero Z bit).\n"); + res=RC_FORMAT; + goto error_reply; + } + if (hdr->rcode!=RC_OK) { + DEBUG_MSG("Bad rcode(%u).\n",hdr->rcode); + return NULL; /* discard (may cause error storms) */ + } + + if (hdr->ancount) { + DEBUG_MSG("Query has a non-empty answer section!\n"); + res=RC_FORMAT; + goto error_reply; + } + + if (hdr->nscount) { + DEBUG_MSG("Query has a non-empty authority section!\n"); + res=RC_FORMAT; + goto error_reply; + } + +#if 0 + /* The following only makes sense if we completely disallow + Extension Mechanisms for DNS (RFC 2671). */ + if (hdr->arcount) { + DEBUG_MSG("Query has a non-empty additional section!\n"); + res=RC_FORMAT; + goto error_reply; + } +#endif + { + unsigned char *ptr; + size_t sz; + uint16_t arcount; + res=decode_query(data,rlen,&ptr,&sz,&ql); + if(res!=RC_OK) { + if(res==RC_TRUNC) { + if(!hdr->tc || llist_isempty(&ql)) { + res=RC_FORMAT; + goto free_ql_error_reply; + } + } + else + goto error_reply; + } + + if ((arcount=ntohs(hdr->arcount))) { + int numoptrr= 0; + DEBUG_MSG("Query has a non-empty additional section: " + "checking for OPT pseudo-RR.\n"); + if(res==RC_TRUNC) { + DEBUG_MSG("Additional section cannot be read due to truncation!\n"); + res=RC_FORMAT; + goto free_ql_error_reply; + } + res=decode_query_additional(data,rlen,ptr,sz,arcount, &numoptrr, &ednsinfo); + if(!(res==RC_OK || (res==RC_TRUNC && hdr->tc))) { + res=RC_FORMAT; + goto free_ql_error_reply; + } + if(numoptrr) { +#if DEBUG>0 + if(numoptrr!=1) { + DEBUG_MSG("Additional section in query contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + if(ednsinfo.version!=0) { + DEBUG_MSG("Query contains unsupported EDNS version %d!\n", ednsinfo.version); + res=RC_BADVERS; + goto free_ql_error_reply; + } + if(ednsinfo.rcode!=0) { + DEBUG_MSG("Query contains non-zero EDNS rcode (%d)!\n", ednsinfo.rcode); + res=RC_FORMAT; + goto free_ql_error_reply; + } + DEBUG_MSG("Query contains OPT pseudosection: EDNS udp size = %u, flag DO=%u\n", + ednsinfo.udpsize, ednsinfo.do_flg); + ednsinfop = &ednsinfo; + if(udp && ednsinfo.udpsize>UDP_BUFSIZE) { + unsigned udpbufsize = global.udpbufsize; + if(udpbufsize > ednsinfo.udpsize) + udpbufsize = ednsinfo.udpsize; + *udp = udpbufsize; + } + } + } + } + +#if DEBUG>0 + if (debug_p) { + if(!llist_isempty(&ql)) { + dns_queryel_t *qe; + DEBUG_MSG("Questions are:\n"); + for (qe=llist_first(&ql); qe; qe=llist_next(qe)) { + DEBUG_RHN_MSG("\tqc=%s (%u), qt=%s (%u), query=\"%s\"\n", + get_cname(qe->qclass),qe->qclass,get_tname(qe->qtype),qe->qtype,RHN2STR(qe->query)); + } + } + else { + DEBUG_MSG("Query contains no questions.\n"); + } + } +#endif + + if (llist_isempty(&ql)) { + res=RC_FORMAT; + goto error_reply; + } + if (!(ans=compose_answer(&ql, hdr, rlenp, ednsinfop, udp, rcodep))) { + /* An out of memory condition or similar could cause NULL output. Send failure notification */ + res=RC_SERVFAIL; + goto free_ql_error_reply; + } + llist_free(&ql); + return ans; + + free_ql_error_reply: + llist_free(&ql); + error_reply: + *rlenp=sizeof(dns_hdr_t); + { + size_t allocsz = sizeof(dns_msg_t); + if(res&~0xf) + allocsz += sizeof_opt_pseudo_rr; + ans= (dns_msg_t *)pdnsd_malloc(allocsz); + if (ans) { + mk_error_reply(hdr->id,rlen>=3?hdr->opcode:OP_QUERY,res,&ans->hdr); + if(res&~0xf) + add_opt_pseudo_rr(&ans,rlenp,&allocsz, + global.udpbufsize,res,0,0); + } + else if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in query processing."); + } + } + if(rcodep) *rcodep= res; + return ans; +} + +/* + * Called by *_answer_thread exit handler to clean up process count. + */ +inline static void decrease_procs() +{ + + pthread_mutex_lock(&proc_lock); + procs--; + qprocs--; + pthread_mutex_unlock(&proc_lock); +} + +static void udp_answer_thread_cleanup(void *data) +{ + pdnsd_free(data); + decrease_procs(); +} + +/* + * A thread opened to answer a query transmitted via udp. Data is a pointer to the structure udp_buf_t that + * contains the received data and various other parameters. + * After the query is answered, the thread terminates + * XXX: data must point to a correctly aligned buffer + */ +static void *udp_answer_thread(void *data) +{ + struct msghdr msg; + struct iovec v; + struct cmsghdr *cmsg; +#if defined(SRC_ADDR_DISC) + char ctrl[CMSG_SPACE(sizeof(pkt_info_t))]; +#endif + size_t rlen=((udp_buf_t *)data)->len; + unsigned udpmaxrespsize = UDP_BUFSIZE; + /* XXX: process_query is assigned to this, this mallocs, so this points to aligned memory */ + dns_msg_t *resp; + int rcode; + unsigned thrid; + pthread_cleanup_push(udp_answer_thread_cleanup, data); + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + for(;;) { + pthread_mutex_lock(&proc_lock); + if (procs0 + if(debug_p) { + int err; + if ((err=pthread_setspecific(thrid_key, &thrid)) != 0) { + if(++da_misc_errs<=MISC_MAX_ERRS) + log_error("pthread_setspecific failed: %s",strerror(err)); + /* pdnsd_exit(); */ + } + } +#endif + + if (!(resp=process_query(((udp_buf_t *)data)->buf,&rlen,&udpmaxrespsize,&rcode))) { + /* + * A return value of NULL is a fatal error that prohibits even the sending of an error message. + * logging is already done. Just exit the thread now. + */ + pthread_exit(NULL); /* data freed by cleanup handler */ + } + pthread_cleanup_push(free, resp); + if (rlen>udpmaxrespsize) { + rlen=udpmaxrespsize; + resp->hdr.tc=1; /*set truncated bit*/ + } + DEBUG_MSG("Outbound msg len %li, tc=%u, rc=\"%s\"\n",(long)rlen,resp->hdr.tc,get_ename(rcode)); + + v.iov_base=(char *)&resp->hdr; + v.iov_len=rlen; + msg.msg_iov=&v; + msg.msg_iovlen=1; +#if (TARGET!=TARGET_CYGWIN) +#if defined(SRC_ADDR_DISC) + msg.msg_control=ctrl; + msg.msg_controllen=sizeof(ctrl); +#else + msg.msg_control=NULL; + msg.msg_controllen=0; +#endif + msg.msg_flags=0; /* to avoid warning message by Valgrind */ +#endif + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + + msg.msg_name=&((udp_buf_t *)data)->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); +# if defined(SRC_ADDR_DISC) +# if (TARGET==TARGET_LINUX) + ((udp_buf_t *)data)->pi.pi4.ipi_spec_dst=((udp_buf_t *)data)->pi.pi4.ipi_addr; + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_pktinfo)); + cmsg->cmsg_level=SOL_IP; + cmsg->cmsg_type=IP_PKTINFO; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.pi4,sizeof(struct in_pktinfo)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in_pktinfo)); +# else + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_addr)); + cmsg->cmsg_level=IPPROTO_IP; + cmsg->cmsg_type=IP_RECVDSTADDR; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.ai4,sizeof(struct in_addr)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in_addr)); +# endif +# endif +# if DEBUG>0 + { + char buf[ADDRSTR_MAXLEN]; + + DEBUG_MSG("Answering to: %s", inet_ntop(AF_INET,&((udp_buf_t *)data)->addr.sin4.sin_addr,buf,ADDRSTR_MAXLEN)); +# if defined(SRC_ADDR_DISC) +# if (TARGET==TARGET_LINUX) + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET,&((udp_buf_t *)data)->pi.pi4.ipi_spec_dst,buf,ADDRSTR_MAXLEN)); +# else + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET,&((udp_buf_t *)data)->pi.ai4,buf,ADDRSTR_MAXLEN)); +# endif +# else + DEBUG_MSGC("\n"); +# endif + } +# endif /* DEBUG */ + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + + msg.msg_name=&((udp_buf_t *)data)->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); +# if defined(SRC_ADDR_DISC) + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level=SOL_IPV6; + cmsg->cmsg_type=IPV6_PKTINFO; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.pi6,sizeof(struct in6_pktinfo)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in6_pktinfo)); +# endif +# if DEBUG>0 + { + char buf[ADDRSTR_MAXLEN]; + + DEBUG_MSG("Answering to: %s", inet_ntop(AF_INET6,&((udp_buf_t *)data)->addr.sin6.sin6_addr,buf,ADDRSTR_MAXLEN)); +# if defined(SRC_ADDR_DISC) + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET6,&((udp_buf_t *)data)->pi.pi6.ipi6_addr,buf,ADDRSTR_MAXLEN)); +# else + DEBUG_MSGC("\n"); +# endif + } +# endif /* DEBUG */ + } +#endif + + /* Lock the socket, and clear the error flag before dropping the lock */ +#ifdef SOCKET_LOCKING + pthread_mutex_lock(&s_lock); +#endif + if (sendmsg(((udp_buf_t *)data)->sock,&msg,0)<0) { +#ifdef SOCKET_LOCKING + pthread_mutex_unlock(&s_lock); +#endif + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Error in udp send: %s",strerror(errno)); + } + } else { + int tmp; + socklen_t sl=sizeof(tmp); + getsockopt(((udp_buf_t *)data)->sock, SOL_SOCKET, SO_ERROR, &tmp, &sl); +#ifdef SOCKET_LOCKING + pthread_mutex_unlock(&s_lock); +#endif + } + + pthread_cleanup_pop(1); /* free(resp) */ + pthread_cleanup_pop(1); /* free(data) */ + return NULL; +} + +int init_udp_socket() +{ + int sock; + int so=1; + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + if ((sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) { + log_error("Could not open udp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(global.port); + sin.sin4.sin_addr=global.a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + if ((sock=socket(PF_INET6,SOCK_DGRAM,IPPROTO_UDP))==-1) { + log_error("Could not open udp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(global.port); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + +#ifdef SRC_ADDR_DISC +# if (TARGET!=TARGET_LINUX) + if (run_ipv4) { +# endif + /* The following must be set on any case because it also applies for IPv4 packets sent to + * ipv6 addresses. */ +# if (TARGET==TARGET_LINUX ) + if (setsockopt(sock,SOL_IP,IP_PKTINFO,&so,sizeof(so))!=0) { +# else + if (setsockopt(sock,IPPROTO_IP,IP_RECVDSTADDR,&so,sizeof(so))!=0) { +# endif + log_error("Could not set options on udp socket: %s",strerror(errno)); + close(sock); + return -1; + } +# if (TARGET!=TARGET_LINUX) + } +# endif + +# ifdef ENABLE_IPV6 + if (!run_ipv4) { + if (setsockopt(sock,SOL_IPV6,IPV6_RECVPKTINFO,&so,sizeof(so))!=0) { + log_error("Could not set options on udp socket: %s",strerror(errno)); + close(sock); + return -1; + } + } +# endif +#endif + if (bind(sock,(struct sockaddr *)&sin,sinl)!=0) { + log_error("Could not bind to udp socket: %s",strerror(errno)); + close(sock); + return -1; + } + return sock; +} + +/* + * Listen on the specified port for udp packets and answer them (each in a new thread to be nonblocking) + * This was changed to support sending UDP packets with exactly the same source address as they were coming + * to us, as required by rfc2181. Although this is a sensible requirement, it is slightly more difficult + * and may introduce portability issues. + */ +void *udp_server_thread(void *dummy) +{ + int sock; + ssize_t qlen; + pthread_t pt; + udp_buf_t *buf; + struct msghdr msg; + struct iovec v; + struct cmsghdr *cmsg; + char ctrl[512]; +#if defined(ENABLE_IPV6) && (TARGET==TARGET_LINUX) + struct in_pktinfo sip; +#endif + /* (void)dummy; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + sock=udp_socket; + + while (1) { + int udpbufsize= global.udpbufsize; + if (!(buf=(udp_buf_t *)pdnsd_calloc(1,sizeof(udp_buf_t)+udpbufsize))) { + if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in request handling."); + } + break; + } + + buf->sock=sock; + + v.iov_base=(char *)buf->buf; + v.iov_len=udpbufsize; + msg.msg_iov=&v; + msg.msg_iovlen=1; +#if (TARGET!=TARGET_CYGWIN) + msg.msg_control=ctrl; + msg.msg_controllen=sizeof(ctrl); +#endif + +#if defined(SRC_ADDR_DISC) +# ifdef ENABLE_IPV4 + if (run_ipv4) { + msg.msg_name=&buf->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); + if ((qlen=recvmsg(sock,&msg,0))>=0) { + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { +# if (TARGET==TARGET_LINUX) + if (cmsg->cmsg_level==SOL_IP && cmsg->cmsg_type==IP_PKTINFO) { + memcpy(&buf->pi.pi4,CMSG_DATA(cmsg),sizeof(struct in_pktinfo)); + break; + } +# else + if (cmsg->cmsg_level==IPPROTO_IP && cmsg->cmsg_type==IP_RECVDSTADDR) { + memcpy(&buf->pi.ai4,CMSG_DATA(cmsg),sizeof(buf->pi.ai4)); + break; + } +# endif + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Could not discover udp destination address"); + } + goto free_buf_continue; + } + } else if (errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } + } +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + msg.msg_name=&buf->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); + if ((qlen=recvmsg(sock,&msg,0))>=0) { + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { + if (cmsg->cmsg_level==SOL_IPV6 && cmsg->cmsg_type==IPV6_PKTINFO) { + memcpy(&buf->pi.pi6,CMSG_DATA(cmsg),sizeof(struct in6_pktinfo)); + break; + } + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + /* We might have an IPv4 Packet incoming on our IPv6 port, so we also have to + * check for IPv4 sender addresses */ + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { +# if (TARGET==TARGET_LINUX) + if (cmsg->cmsg_level==SOL_IP && cmsg->cmsg_type==IP_PKTINFO) { + memcpy(&sip,CMSG_DATA(cmsg),sizeof(sip)); + IPV6_MAPIPV4(&sip.ipi_addr,&buf->pi.pi6.ipi6_addr); + buf->pi.pi6.ipi6_ifindex=sip.ipi_ifindex; + break; + } + /* FIXME: What about BSD? probably ok, but... */ +# endif + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Could not discover udp destination address"); + } + goto free_buf_continue; + } + } + } else if (errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } + } +# endif +#else /* !SRC_ADDR_DISC */ +# ifdef ENABLE_IPV4 + if (run_ipv4) { + msg.msg_name=&buf->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); + } +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + msg.msg_name=&buf->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); + } +# endif + qlen=recvmsg(sock,&msg,0); + if (qlen<0 && errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } +#endif /* SRC_ADDR_DISC */ + + if (qlen>=0) { + pthread_mutex_lock(&proc_lock); + if (qprocslen=qlen; + err=pthread_create(&pt,&attr_detached,udp_answer_thread,(void *)buf); + if(err==0) + continue; + if(++da_thrd_errs<=THRD_MAX_ERRS) + log_warn("pthread_create failed: %s",strerror(err)); + /* If thread creation failed, free resources associated with it. */ + pthread_mutex_lock(&proc_lock); + --qprocs; --spawned; + } + ++dropped; + pthread_mutex_unlock(&proc_lock); + } + free_buf_continue: + pdnsd_free(buf); + usleep_r(50000); + } + + udp_socket=-1; + close(sock); + udps_thrid=main_thrid; + if (tcp_socket==-1) + pdnsd_exit(); + return NULL; +} + +#ifndef NO_TCP_SERVER + +static void tcp_answer_thread_cleanup(void *csock) +{ + close(*((int *)csock)); + pdnsd_free(csock); + decrease_procs(); +} + +/* + * Process a dns query via tcp. The argument is a pointer to the socket. + */ +static void *tcp_answer_thread(void *csock) +{ + /* XXX: This should be OK, the original must be (and is) aligned */ + int sock=*((int *)csock); + unsigned thrid; + + pthread_cleanup_push(tcp_answer_thread_cleanup, csock); + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + for(;;) { + pthread_mutex_lock(&proc_lock); + if (procs0 + if(debug_p) { + int err; + if ((err=pthread_setspecific(thrid_key, &thrid)) != 0) { + if(++da_misc_errs<=MISC_MAX_ERRS) + log_error("pthread_setspecific failed: %s",strerror(err)); + /* pdnsd_exit(); */ + } + } +#endif +#ifdef TCP_SUBSEQ + + /* rfc1035 says we should process multiple queries in succession, so we are looping until + * the socket is closed by the other side or by tcp timeout. + * This in fact makes DoSing easier. If that is your concern, you should disable pdnsd's + * TCP server.*/ + for(;;) +#endif + { + int rlen,olen; + size_t nlen; + unsigned char *buf; + dns_msg_t *resp; + +#ifdef NO_POLL + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + PDNSD_ASSERT(sock=2) { /* We need the id to send a valid reply. */ + dns_msg_t err; + mk_error_reply(((dns_hdr_t*)buf)->id, + olen>=3?((dns_hdr_t*)buf)->opcode:OP_QUERY, + RC_FORMAT, + &err.hdr); + err.len=htons(sizeof(dns_hdr_t)); + write_all(sock,&err,sizeof(err)); /* error anyway. */ + } + pthread_exit(NULL); /* buf freed and socket closed by cleanup handlers */ + } + olen += rv; + } + nlen=rlen; + if (!(resp=process_query(buf,&nlen,NULL,NULL))) { + /* + * A return value of NULL is a fatal error that prohibits even the sending of an error message. + * logging is already done. Just exit the thread now. + */ + pthread_exit(NULL); + } + pthread_cleanup_pop(1); /* free(buf) */ + pthread_cleanup_push(free,resp); + { + int err; size_t rsize; + resp->len=htons(nlen); + rsize=dnsmsghdroffset+nlen; + if ((err=write_all(sock,resp,rsize))!=rsize) { + DEBUG_MSG("Error while writing to TCP client: %s\n",err==-1?strerror(errno):"unknown error"); + pthread_exit(NULL); /* resp is freed and socket is closed by cleanup handlers */ + } + } + pthread_cleanup_pop(1); /* free(resp) */ + } + + /* socket is closed by cleanup handler */ + pthread_cleanup_pop(1); + return NULL; +} + +int init_tcp_socket() +{ + int sock; + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + if ((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) { + log_error("Could not open tcp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(global.port); + sin.sin4.sin_addr=global.a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + if ((sock=socket(PF_INET6,SOCK_STREAM,IPPROTO_TCP))==-1) { + log_error("Could not open tcp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(global.port); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + { + int so=1; + /* The SO_REUSEADDR socket option tells the kernel that even if this port + is busy (in the TIME_WAIT state), go ahead and reuse it anyway. If it + is busy, but with another state, we should get an address already in + use error. It is useful if pdnsd is shut down, and then restarted right + away while sockets are still active on its port. There is a slight risk + though. If unexpected data comes in, it may confuse pdnsd, but while + this is possible, it is not likely. + */ + if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&so,sizeof(so))) + log_warn("Could not set options on tcp socket: %s",strerror(errno)); + } + if (bind(sock,(struct sockaddr *)&sin,sinl)) { + log_error("Could not bind tcp socket: %s",strerror(errno)); + close(sock); + return -1; + } + return sock; +} + +/* + * Listen on the specified port for tcp connects and answer them (each in a new thread to be nonblocking) + */ +void *tcp_server_thread(void *p) +{ + int sock; + pthread_t pt; + int *csock; + + /* (void)p; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + sock=tcp_socket; + + if (listen(sock,5)) { + if (++da_tcp_errs<=TCP_MAX_ERRS) { + log_error("Could not listen on tcp socket: %s",strerror(errno)); + } + goto close_sock_return; + } + + while (1) { + if (!(csock=(int *)pdnsd_malloc(sizeof(int)))) { + if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in request handling."); + } + break; + } + if ((*csock=accept(sock,NULL,0))==-1) { + if (errno!=EINTR && ++da_tcp_errs<=TCP_MAX_ERRS) { + log_error("tcp accept failed: %s",strerror(errno)); + } + } else { + /* + * With creating a new thread, we follow recommendations + * in rfc1035 not to block + */ + pthread_mutex_lock(&proc_lock); + if (qprocs. +*/ + + +#ifndef DNS_ANSWER_H +#define DNS_ANSWER_H + +#include + +/* --- from main.c */ +extern pthread_t main_thrid,servstat_thrid,statsock_thrid,tcps_thrid,udps_thrid; +extern volatile int tcp_socket; +extern volatile int udp_socket; +/* --- */ + +int init_udp_socket(void); +int init_tcp_socket(void); +void start_dns_servers(void); +int report_thread_stat(int f); + +#endif diff --git a/app/src/main/jni/pdnsd/src/dns_query.c b/app/src/main/jni/pdnsd/src/dns_query.c new file mode 100644 index 0000000..0b6c9c0 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/dns_query.c @@ -0,0 +1,3798 @@ +/* dns_query.c - Execute outgoing dns queries and write entries to cache + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "consts.h" +#include "ipvers.h" +#include "dns_query.h" +#include "cache.h" +#include "dns.h" +#include "conff.h" +#include "servers.h" +#include "helpers.h" +#include "netdev.h" +#include "error.h" +#include "debug.h" + + +#if defined(NO_TCP_QUERIES) && M_PRESET!=UDP_ONLY +# error "You may not define NO_TCP_QUERIES when M_PRESET is not set to UDP_ONLY" +#endif +#if defined(NO_UDP_QUERIES) && M_PRESET!=TCP_ONLY +# error "You may not define NO_UDP_QUERIES when M_PRESET is not set to TCP_ONLY" +#endif + +/* data type to hold lists of IP addresses (both v4 and v6) + The allocated size should be: + sizeof(rejectlist_t) + na4*sizeof(addr4maskpair_t) + na6*sizeof(addr6maskpair_t) +*/ +typedef struct rejectlist_s { + struct rejectlist_s *next; + short policy; + short inherit; + int na4; +#if ALLOW_LOCAL_AAAA + int na6; + addr6maskpair_t rdata[0]; /* dummy array for alignment */ +#else + addr4maskpair_t rdata[0]; +#endif +} rejectlist_t; + +/* --- structures and state constants for parallel query */ +typedef struct { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } a; +#ifdef ENABLE_IPV6 + struct in_addr a4fallback; +#endif + time_t timeout; + unsigned short flags; + short state; + short qm; + char nocache; + char auth_serv; + char lean_query; + char edns_query; + char needs_testing; + char trusted; + char aa; + char tc; + char ra; + char failed; + const unsigned char *nsdomain; + rejectlist_t *rejectlist; + /* internal state for p_exec_query */ + int sock; +#if 0 + dns_cent_t nent; + dns_cent_t servent; +#endif + unsigned short transl; + unsigned short recvl; +#ifndef NO_TCP_QUERIES + int iolen; /* number of bytes written or read up to now */ +#endif + dns_msg_t *msg; + dns_hdr_t *recvbuf; + unsigned short myrid; + int s_errno; +} query_stat_t; +typedef DYNAMIC_ARRAY(query_stat_t) *query_stat_array; + +/* Some macros for handling data in reject lists + Perhaps we should use inline functions instead of macros. +*/ +#define have_rejectlist(st) ((st)->rejectlist!=NULL) +#define inherit_rejectlist(st) ((st)->rejectlist && (st)->rejectlist->inherit) +#define reject_policy(st) ((st)->rejectlist->policy) +#define nreject_a4(st) ((st)->rejectlist->na4) +#if ALLOW_LOCAL_AAAA +#define nreject_a6(st) ((st)->rejectlist->na6) +#define rejectlist_a6(st) ((addr6maskpair_t *)(st)->rejectlist->rdata) +#define rejectlist_a4(st) ((addr4maskpair_t *)(rejectlist_a6(st)+nreject_a6(st))) +#else +#define rejectlist_a4(st) ((addr4maskpair_t *)(st)->rejectlist->rdata) +#endif + +#define QS_INITIAL 0 /* This is the initial state. Set this before starting. */ + +#define QS_TCPINITIAL 1 /* Start a TCP query. */ +#define QS_TCPWRITE 2 /* Waiting to write data. */ +#define QS_TCPREAD 3 /* Waiting to read data. */ + +#define QS_UDPINITIAL 4 /* Start a UDP query */ +#define QS_UDPRECEIVE 5 /* UDP query transmitted, waiting for response. */ + +#define QS_QUERY_CASES case QS_TCPINITIAL: case QS_TCPWRITE: case QS_TCPREAD: case QS_UDPINITIAL: case QS_UDPRECEIVE + +#define QS_CANCELED 7 /* query was started, but canceled before completion */ +#define QS_DONE 8 /* done, resources freed, result is in stat_t */ + + +/* Events to be polled/selected for */ +#define QS_WRITE_CASES case QS_TCPWRITE +#define QS_READ_CASES case QS_TCPREAD: case QS_UDPRECEIVE + +/* + * This is for error handling to prevent spewing the log files. + * Races do not really matter here, so no locks. + */ +#define MAXPOLLERRS 10 +static volatile unsigned long poll_errs=0; + +#define SOCK_ADDR(p) ((struct sockaddr *) &(p)->a) + +#ifdef SIN_LEN +#undef SIN_LEN +#endif + +#define SIN_LEN SEL_IPVER(sizeof(struct sockaddr_in),sizeof(struct sockaddr_in6)) +#define PDNSD_A(p) SEL_IPVER(((pdnsd_a *) &(p)->a.sin4.sin_addr),((pdnsd_a *) &(p)->a.sin6.sin6_addr)) + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +typedef DYNAMIC_ARRAY(dns_cent_t) *dns_cent_array; + + +/* + * Take the data from an RR and add it to an array of cache entries. + * The return value will be RC_OK in case of success, + * RC_SERVFAIL in case there is a problem with inconsistent ttl timestamps + * or RC_FATALERR in case of a memory allocation failure. + */ +static int rr_to_cache(dns_cent_array *centa, unsigned char *oname, int tp, time_t ttl, + unsigned dlen, void *data, unsigned flags, time_t queryts) +{ + int i,n; + dns_cent_t *cent; + + n=DA_NEL(*centa); + for(i=0;iqname,oname)) { + int retval=RC_OK; + /* We already have an entry in the array for this name. add_cent_rr is sufficient. + However, make sure there are no double records. This is done by add_cent_rr */ +#ifdef RFC2181_ME_HARDER + rr_set_t *rrset= getrrset(cent,tp); + if (rrset && rrset->ttl!=ttl) + retval= RC_SERVFAIL; +#endif + return add_cent_rr(cent,tp,ttl,queryts,flags,dlen,data DBG1)? retval: RC_FATALERR; + } + } + + /* Add a new entry to the array for this name. */ + if (!(*centa=DA_GROW1_F(*centa,free_cent0))) + return RC_FATALERR; + cent=&DA_LAST(*centa); + if (!init_cent(cent,oname, 0, 0, 0 DBG1)) { + *centa=DA_RESIZE(*centa,n); + return RC_FATALERR; + } + return add_cent_rr(cent,tp,ttl,queryts,flags,dlen,data DBG1)? RC_OK: RC_FATALERR; +} + +/* + * Takes a pointer (ptr) to a buffer with recnum rrs,decodes them and enters + * them into an array of cache entries. *ptr is modified to point after the last + * rr, and *lcnt is decremented by the size of the rrs. + * + * *numopt is incremented with the number of OPT pseudo RRs found (should be at most one). + * The structure pointed to by ep is filled with the information of the first OPT pseudo RR found, + * but only if *numopt was set to zero before the call. + * + * The return value will be either RC_OK (which indicates success), + * or one of the failure codes RC_FORMAT, RC_TRUNC, RC_SERVFAIL or RC_FATALERR + * (the latter indicates a memory allocation failure). +*/ +static int rrs2cent(unsigned char *msg, size_t msgsz, unsigned char **ptr, size_t *lcnt, int recnum, + unsigned flags, time_t queryts, dns_cent_array *centa, int *numopt, edns_info_t *ep) +{ + int rc, retval=RC_OK; + int i; + uint16_t type,class; uint32_t ttl; uint16_t rdlength; + + for (i=0;i0 + if(oname[0]!=0) { + DEBUG_MSG("rrs2cent: name in OPT record not empty!\n"); + } +#endif + ep->udpsize= class; + ep->rcode= ((uint16_t)ttlp[0]<<4) | ((dns_hdr_t *)msg)->rcode; + ep->version= ttlp[1]; + ep->do_flg= (ttlp[2]>>7)&1; +#if DEBUG>0 + if(debug_p) { + unsigned int Zflags= ((uint16_t)ttlp[2]<<8) | ttlp[3]; + if(Zflags & 0x7fff) { + DEBUG_MSG("rrs2cent: Z field contains unknown nonzero bits (%04x).\n", + Zflags); + } + } + if(rdlength) { + DEBUG_MSG("rrs2cent: RDATA field in OPT record not empty!\n"); + } +#endif + } + else { + DEBUG_MSG("rrs2cent: ingnoring surplus OPT record.\n"); + } + } + else if (!(PDNSD_NOT_CACHED_TYPE(type) || class!=C_IN)) { + /* Some types contain names that may be compressed, so these need to be processed. + * The other records are taken as they are. */ + + size_t blcnt=rdlength; + unsigned char *bptr=*ptr; /* make backup for decompression, because rdlength is the + authoritative record length and pointer and size will be + modified by decompress_name. */ + unsigned char *nptr; + unsigned int slen; + + switch (type) { + case T_A: + /* Validate types we use internally */ + if(rdlength!=4) goto invalid_length; + goto default_case; + + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + { + unsigned char db[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, db, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, len, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + { + unsigned char db[DNSNAMEBUFSIZE+DNSNAMEBUFSIZE]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_MINFO/T_RP: buffer limit reached"); */ + nptr+=len; + slen=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + { + unsigned char db[2+DNSNAMEBUFSIZE]; + if (blcnt<2) + goto record_too_short; + memcpy(db,bptr,2); /* copy the preference field*/ + blcnt-=2; + bptr+=2; + nptr=db+2; + slen=2; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + + case T_SOA: + { + unsigned char db[DNSNAMEBUFSIZE+DNSNAMEBUFSIZE+20]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_SOA: buffer limit reached"); */ + nptr+=len; + slen=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + nptr+=len; + slen+=len; + /* PDNSD_ASSERT(slen + 20 <= sizeof(db), "T_SOA: buffer limit reached"); */ + if (blcnt<20) + goto record_too_short; + memcpy(nptr,bptr,20); /*copy the rest of the SOA record*/ + blcnt-=20; + slen+=20; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#if IS_CACHED_AAAA + case T_AAAA: + /* Validate types we use internally */ + if(rdlength!=16) goto invalid_length; + goto default_case; +#endif +#if IS_CACHED_PX + case T_PX: + { + unsigned char db[2+DNSNAMEBUFSIZE+DNSNAMEBUFSIZE]; + if (blcnt<2) + goto record_too_short; + memcpy(db,bptr,2); /* copy the preference field*/ + blcnt-=2; + bptr+=2; + nptr=db+2; + slen=2; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_PX: buffer limit reached"); */ + nptr+=len; + slen+=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* nptr+=len; */ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + { + unsigned char db[6+DNSNAMEBUFSIZE]; + if (blcnt<6) + goto record_too_short; + memcpy(db,bptr,6); + blcnt-=6; + bptr+=6; + nptr=db+6; + slen=6; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + { + unsigned char db[1040]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + nptr+=len; + slen=len+blcnt; + if (slen > sizeof(db)) + goto buffer_overflow; + memcpy(nptr,bptr,blcnt); + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + { + int j; + unsigned char db[4 + 3*256 + DNSNAMEBUFSIZE]; + nptr=db; + /* + * After the preference field, three text strings follow, the maximum length being 255 + * characters for each (this is ensured by the type of *bptr), plus one length byte for + * each, so 3 * 256 = 786 in total. In addition, the name below is up to DNSNAMEBUFSIZE characters + * in size, and the preference field is another 4 bytes in size, so the total length + * that can be taken up is 1028 characters. This means that the whole record will always + * fit into db. + */ + len=4; /* also copy the preference field*/ + for (j=0;j<3;j++) { + if (len>=blcnt) + goto record_too_short; + len += ((unsigned)bptr[len])+1; + } + if(len>blcnt) + goto record_too_short; + memcpy(nptr,bptr,len); + blcnt-=len; + bptr+=len; + nptr+=len; + slen=len; + + /* PDNSD_ASSERT(slen+DNSNAMEBUFSIZE <= sizeof(db), "T_NAPTR: buffer limit reached (name)"); */ + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_IPSECKEY + case T_IPSECKEY: + { + unsigned gwtp; + /* An IPSECKEY record can contain a domain name, so we do some sanity checks just to be sure. */ + if(blcnt<3) goto record_too_short; + gwtp= bptr[1]; + blcnt -= 3; + bptr += 3; + switch(gwtp) { + case 0: goto default_case; + case 1: /* There should be enough room for IPv4 address. */ + if(blcnt<4) goto record_too_short; + goto default_case; + case 2: /* There should be enough room for IPv6 address. */ + if(blcnt<16) goto record_too_short; + goto default_case; + case 3: /* Check that domain name is not compressed. */ + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4025 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=3+len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,*ptr,3); + nptr=mempcpy(nptr,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + default: + DEBUG_MSG("rrs2cent: %s record contains unsupported gateway type (%u).\n",getrrtpname(type),gwtp); + return RC_FORMAT; + } + } + break; +#endif +#if IS_CACHED_RRSIG + case T_RRSIG: + /* An RRSIG record contains a domain name, so we do some sanity checks just to be sure. */ + if(blcnt<18) goto record_too_short; + blcnt -= 18; + bptr += 18; + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4034 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=18+len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,*ptr,18); + nptr=mempcpy(nptr,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NSEC + case T_NSEC: + /* An NSEC record contains a domain name, so we do some sanity checks just to be sure. */ + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4034 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif + default: + default_case: + if ((rc=rr_to_cache(centa, oname, type, ttl, rdlength, *ptr, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + } + else { + /* skip otherwise */ + DEBUG_MSG("rrs2cent: ignoring record of type %s (%d), class %s (%d).\n", + getrrtpname(type), type, + class==C_IN?"IN":"[unknown]", class); + } + + *lcnt -= rdlength; + *ptr += rdlength; + } + return retval; + + trailing_junk: + DEBUG_MSG("rrs2cent: %s record has trailing junk.\n",getrrtpname(type)); + return RC_FORMAT; + + record_too_short: + DEBUG_MSG("rrs2cent: %s record too short.\n",getrrtpname(type)); + return RC_FORMAT; + + buffer_overflow: + DEBUG_MSG("rrs2cent: buffer too small to process %s record.\n",getrrtpname(type)); + return RC_FORMAT; + + invalid_length: + DEBUG_MSG("rrs2cent: %s record has length %u.\n",getrrtpname(type),rdlength); + return RC_FORMAT; +} + +/* + * Try to bind the socket to a port in the given port range. Returns 1 on success, or 0 on failure. + */ +static int bind_socket(int s) +{ + int query_port_start=global.query_port_start,query_port_end=global.query_port_end; + + /* + * -1, as a special value for query_port_start, denotes that we let the kernel select + * a port when we first use the socket, which used to be the default. + */ + if (query_port_start >= 0) { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + int prt, pstart, range = query_port_end-query_port_start+1, m=0xffff; + unsigned try1,try2, maxtry2; + + if (range<=0 || range>0x10000) { + log_warn("Illegal port range in %s line %d, dropping query!\n",__FILE__,__LINE__); + return 0; + } + if(range<=0x8000) { + /* Find the smallest power of 2 >= range. */ + for(m=1; m= range. */ + for(try1=0;;) { + prt= get_rand16()&m; + if(prt=0x10000) { + log_warn("Cannot get random number < range" + " after %d tries in %s line %d," + " bad random number generator?\n", + try1,__FILE__,__LINE__); + return 0; + } + } + prt += query_port_start; + + for(pstart=prt;;) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(prt); + sin.sin4.sin_addr=global.out_a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(prt); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.out_a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + if (bind(s,(struct sockaddr *)&sin,sinl)==-1) { + if (errno!=EADDRINUSE && + errno!=EADDRNOTAVAIL) { /* EADDRNOTAVAIL should not happen here... */ + log_warn("Could not bind to socket: %s\n", strerror(errno)); + return 0; + } + /* If the address is in use, we continue. */ + } else + goto done; + + if(++try2>=maxtry2) { + /* It is possible we missed the free ports by chance, + try scanning the whole range. */ + if (++prt>query_port_end) + prt=query_port_start; + if (prt==pstart) { + /* Wrapped around, scanned the whole range. Give up. */ + log_warn("Out of ports in the range" + " %d-%d, dropping query!\n", + query_port_start,query_port_end); + return 0; + } + } + else /* Try new random number */ + break; + } + } + } +done: + return 1; +} + + +inline static void *realloc_or_cleanup(void *ptr,size_t size) +{ + void *retval=pdnsd_realloc(ptr,size); + if(!retval) + pdnsd_free(ptr); + return retval; +} + +#if defined(NO_TCP_QUERIES) +# define USE_UDP(st) 1 +#elif defined(NO_UDP_QUERIES) +# define USE_UDP(st) 0 +#else /* !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) */ +# define USE_UDP(st) ((st)->qm==UDP_ONLY || (st)->qm==UDP_TCP) + +/* These functions will be used in case a TCP query might fail and we want to try again using UDP. */ + +# define tentative_tcp_query(st) ((st)->qm==TCP_UDP && ((st)->state==QS_TCPWRITE || ((st)->state==QS_TCPREAD && (st)->iolen==0))) + +inline static void switch_to_udp(query_stat_t *st) +{ + st->qm=UDP_ONLY; + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->state=QS_UDPINITIAL; + /* st->failed=0; */ +} + +/* This function will be used in case a UDP reply was truncated and we want to try again using TCP. */ + +inline static void switch_to_tcp(query_stat_t *st) +{ + /* PDNSD_ASSERT(st->state==QS_INITIAL || st->state==QS_DONE || st->state==QS_CANCELED, + "Attempt to switch to TCP while a query is in progress."); */ + st->qm=TCP_ONLY; + st->state=QS_INITIAL; + st->failed=0; +} +#endif + + +/* ------ following is the parallel query code. + * It has been observed that a whole lot of name servers are just damn lame, with response time + * of about 1 min. If that slow one is by chance the first server we try, serializing the tries is quite + * sub-optimal. Also when doing serial queries, the timeout values given in the config will add up, which + * is not the Right Thing. Now that serial queries are in place, this is still true for CNAME recursion, + * and for recursion in quest for the holy AA, but not totally for querying multiple servers. + * The impact on network bandwith should be only marginal (given todays bandwith). + * + * The actual strategy is to do (max) PAR_QUERIES parallel queries, and, if these time out or fail, do again + * that number of queries, until we are successful or there are no more servers to query. + * Since the memory footprint of a thread is considerably large on some systems, and because we have better + * control, we will do the parallel queries multiplexed in one thread. + */ + +/* The query state machine that is called from p_exec_query. This is called once for initialization (state + * QS_TCPINITIAL or QS_UDPINITIAL is preset), and the state that it gives back may either be state QS_DONE, + * in which case it must return a return code other than -1 and is called no more for this server + * (except perhaps in UDP mode if TCP failed). If p_query_sm returns -1, then the state machine is in a read + * or write state, and a function higher up the calling chain can setup a poll() or select() together with st->sock. + * If that poll/select is succesful for that socket, p_exec_query is called again and will hand over to p_query_sm. + * So, you can assume that read(), write() and recvfrom() will not block at the start of a state handling when you + * have returned -1 (which means "call again") as last step of the last state handling. */ +static int p_query_sm(query_stat_t *st) +{ + int retval=RC_SERVFAIL,rv; + +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + tryagain: +#endif + switch (st->state){ + /* TCP query code */ +#ifndef NO_TCP_QUERIES + case QS_TCPINITIAL: + if ((st->sock=socket(PDNSD_PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) { + DEBUG_MSG("Could not open socket: %s\n", strerror(errno)); + break; + } + /* sin4 or sin6 is intialized, hopefully. */ + + /* maybe bind */ + if (!bind_socket(st->sock)) { + close(st->sock); + break; + } + + /* transmit query by tcp*/ + /* make the socket non-blocking */ + { + int oldflags = fcntl(st->sock, F_GETFL, 0); + if (oldflags == -1 || fcntl(st->sock,F_SETFL,oldflags|O_NONBLOCK)==-1) { + DEBUG_PDNSDA_MSG("fcntl error while trying to make socket to %s non-blocking: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + } + st->iolen=0; +#ifdef ENABLE_IPV6 + retry_tcp_connect: +#endif + if (connect(st->sock,SOCK_ADDR(st),SIN_LEN)==-1) { + if (errno==EINPROGRESS || errno==EPIPE) { + st->state=QS_TCPWRITE; + /* st->event=QEV_WRITE; */ /* wait for writability; the connect is then done */ + return -1; + } else if (errno==ECONNREFUSED) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("TCP connection refused by %s\n", PDNSDA2STR(PDNSD_A(st))); + close(st->sock); + goto tcp_failed; /* We may want to try again using UDP */ + } else { + /* Since immediate connect() errors do not cost any time, we do not try to switch the + * server status to offline */ +#ifdef ENABLE_IPV6 + /* if IPv6 connectivity is for some reason unavailable, perhaps the + IPv4 fallback address can still be reached. */ + if(!run_ipv4 && (errno==ENETUNREACH || errno==ENETDOWN) + && st->a4fallback.s_addr!=INADDR_ANY) + { +#if DEBUG>0 + char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Connecting to %s failed: %s, retrying with IPv4 address %s\n", + PDNSDA2STR(PDNSD_A(st)),strerror(errno), + inet_ntop(AF_INET,&st->a4fallback,abuf,sizeof(abuf))); +#endif + IPV6_MAPIPV4(&st->a4fallback,&st->a.sin6.sin6_addr); + st->a4fallback.s_addr=INADDR_ANY; + goto retry_tcp_connect; + } +#endif + DEBUG_PDNSDA_MSG("Error while connecting to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + } + st->state=QS_TCPWRITE; + /* st->event=QEV_WRITE; */ + /* fall through in case of not EINPROGRESS */ + case QS_TCPWRITE: + { + int rem= dnsmsghdroffset + st->transl - st->iolen; + if(rem>0) { + rv=write(st->sock,((unsigned char*)st->msg)+st->iolen,rem); + if(rv==-1) { + if(errno==EWOULDBLOCK) + return -1; + st->s_errno=errno; + close(st->sock); + if (st->iolen==0 && + (st->s_errno==ECONNREFUSED || st->s_errno==ECONNRESET || + st->s_errno==EPIPE)) + { + /* This error may be delayed from connect() */ + DEBUG_PDNSDA_MSG("TCP connection to %s failed: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(st->s_errno)); + goto tcp_failed; /* We may want to try again using UDP */ + } + DEBUG_PDNSDA_MSG("Error while sending data to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(st->s_errno)); + break; + } + st->iolen += rv; + if(rvstate=QS_TCPREAD; + st->iolen=0; + /* st->event=QEV_READ; */ + /* fall through */ + case QS_TCPREAD: + if(st->iolen==0) { + uint16_t recvl_net; + rv=read(st->sock,&recvl_net,sizeof(recvl_net)); + if(rv==-1 && errno==EWOULDBLOCK) + return -1; + if(rv!=sizeof(recvl_net)) + goto error_receiv_data; + st->iolen=rv; + st->recvl=ntohs(recvl_net); + if(!(st->recvbuf=(dns_hdr_t *)realloc_or_cleanup(st->recvbuf,st->recvl))) { + close(st->sock); + DEBUG_MSG("Out of memory in query.\n"); + retval=RC_FATALERR; + break; + } + } + { + int offset=st->iolen-sizeof(uint16_t); + int rem=st->recvl-offset; + if(rem>0) { + rv=read(st->sock,((unsigned char*)st->recvbuf)+offset,rem); + if(rv==-1) { + if(errno==EWOULDBLOCK) + return -1; + goto error_receiv_data; + } + if(rv==0) + goto error_receiv_data; /* unexpected EOF */ + st->iolen += rv; + if(rvsock); + st->state=QS_DONE; + return RC_OK; + error_receiv_data: + if(rv==-1) st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while receiving data from %s: %s\n", PDNSDA2STR(PDNSD_A(st)), + rv==-1?strerror(errno):(rv==0 && st->iolen==0)?"no data":"incomplete data"); + close(st->sock); + tcp_failed: +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(st->qm==TCP_UDP) { + switch_to_udp(st); + DEBUG_PDNSDA_MSG("TCP query to %s failed. Trying to use UDP.\n", PDNSDA2STR(PDNSD_A(st))); + goto tryagain; + } +#endif + break; +#endif + +#ifndef NO_UDP_QUERIES + /* UDP query code */ + case QS_UDPINITIAL: + if ((st->sock=socket(PDNSD_PF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) { + DEBUG_MSG("Could not open socket: %s\n", strerror(errno)); + break; + } + + /* maybe bind */ + if (!bind_socket(st->sock)) { + close(st->sock); + break; + } + + /* connect */ +#ifdef ENABLE_IPV6 + retry_udp_connect: +#endif + if (connect(st->sock,SOCK_ADDR(st),SIN_LEN)==-1) { + if (errno==ECONNREFUSED) st->s_errno=errno; +#ifdef ENABLE_IPV6 + /* if IPv6 connectivity is for some reason unavailable, perhaps the + IPv4 fallback address can still be reached. */ + else if(!run_ipv4 && (errno==ENETUNREACH || errno==ENETDOWN) + && st->a4fallback.s_addr!=INADDR_ANY) + { +#if DEBUG>0 + char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Connecting to %s failed: %s, retrying with IPv4 address %s\n", + PDNSDA2STR(PDNSD_A(st)),strerror(errno), + inet_ntop(AF_INET,&st->a4fallback,abuf,sizeof(abuf))); +#endif + IPV6_MAPIPV4(&st->a4fallback,&st->a.sin6.sin6_addr); + st->a4fallback.s_addr=INADDR_ANY; + goto retry_udp_connect; + } +#endif + DEBUG_PDNSDA_MSG("Error while connecting to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + + /* transmit query by udp*/ + /* send will hopefully not block on a freshly opened socket (the buffer + * must be empty) */ + if (send(st->sock,&st->msg->hdr,st->transl,0)==-1) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while sending data to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + st->state=QS_UDPRECEIVE; + /* st->event=QEV_READ; */ + return -1; + case QS_UDPRECEIVE: + { + int udpbufsize= (st->edns_query?global.udpbufsize:UDP_BUFSIZE); + if(!(st->recvbuf=(dns_hdr_t *)realloc_or_cleanup(st->recvbuf,udpbufsize))) { + close(st->sock); + DEBUG_MSG("Out of memory in query.\n"); + retval=RC_FATALERR; + break; + } + if ((rv=recv(st->sock,st->recvbuf,udpbufsize,0))==-1) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while receiving data from %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + st->recvl=rv; + if (st->recvlrecvbuf->id)!=st->myrid) { + DEBUG_MSG("Bad answer received. Ignoring it.\n"); + /* no need to care about timeouts here. That is done at an upper layer. */ + st->state=QS_UDPRECEIVE; + /* st->event=QEV_READ; */ + return -1; + } + close(st->sock); + st->state=QS_DONE; + return RC_OK; + } +#endif + } + + /* If we get here, something has gone wrong. */ + st->state=QS_DONE; + return retval; /* should be either RC_SERVFAIL or RC_FATALERR */ +} + +static dns_cent_t *lookup_cent_array(dns_cent_array ca, const unsigned char *nm) +{ + int i,n=DA_NEL(ca); + for(i=0;iqname,nm)) + return ce; + } + return NULL; +} + +/* Extract the minimum ttl field from the SOA record stored in an rr bucket. */ +static time_t soa_minimum(rr_bucket_t *rrs) +{ + uint32_t minimum; + unsigned char *p=(unsigned char *)(rrs->data); + + /* Skip owner and maintainer. Lengths are validated in cache. */ + p=skiprhn(skiprhn(p)); + /* Skip serial, refresh, retry, expire fields. */ + p += 4*sizeof(uint32_t); + GETINT32(minimum,p); + return minimum; +} + +/* + * The function that will actually execute a query. It takes a state structure in st. + * st->state must be set to QS_INITIAL before calling. + * This may return one of the RC_* codes, where RC_OK indicates success, the other + * RC codes indicate the appropriate errors. -1 is the return value that indicates that + * you should call p_exec_query again with the same state for the result until you get + * a return value >0. Alternatively, call p_cancel_query to cancel it. + * Timeouts are already handled by this function. + * Any records that the query has yielded and that are not a direct answer to the query + * (i.e. are records for other domains) are added to the cache, while the direct answers + * are returned in ent. + * All ns records, to whomever they might belong, are additionally returned in the ns list. + * Free it when done. + * This function calls another query state machine function that supports TCP and UDP. + * + * If you want to tell me that this function has a truly ugly coding style, ah, well... + * You are right, somehow, but I feel it is conceptually elegant ;-) + */ +static int p_exec_query(dns_cent_t **entp, const unsigned char *name, int thint, + query_stat_t *st, dlist *ns, unsigned char *c_soa) +{ + int rv,rcode; + unsigned short rd; + + switch (st->state){ + case QS_INITIAL: { + size_t transl,allocsz; + unsigned int rrnlen=0; + + allocsz= sizeof(dns_msg_t); + if(name) { + rrnlen=rhnlen(name); + allocsz += rrnlen+4; + if(st->edns_query) + allocsz += sizeof_opt_pseudo_rr; + } + st->msg=(dns_msg_t *)pdnsd_malloc(allocsz); + if (!st->msg) { + st->state=QS_DONE; + return RC_FATALERR; /* unrecoverable error */ + } + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->msg->hdr.qr=QR_QUERY; + st->msg->hdr.opcode=OP_QUERY; + st->msg->hdr.aa=0; + st->msg->hdr.tc=0; + st->msg->hdr.rd=(name && st->trusted); + st->msg->hdr.ra=0; + st->msg->hdr.z=0; + st->msg->hdr.ad=0; + st->msg->hdr.cd=0; + st->msg->hdr.rcode=RC_OK; + st->msg->hdr.qdcount=htons(name!=NULL); + st->msg->hdr.ancount=0; + st->msg->hdr.nscount=0; + st->msg->hdr.arcount=0; + + transl= sizeof(dns_hdr_t); + if(name) { + unsigned char *p = mempcpy((unsigned char *)(&st->msg->hdr+1),name,rrnlen); + unsigned short qtype=(st->lean_query?thint:QT_ALL); + PUTINT16(qtype,p); + PUTINT16(C_IN,p); + transl += rrnlen+4; + if(st->edns_query) + add_opt_pseudo_rr(&st->msg,&transl,&allocsz, + global.udpbufsize,RC_OK,0,0); + } + st->transl=transl; +#ifndef NO_TCP_QUERIES + st->msg->len=htons(st->transl); +#endif + st->recvbuf=NULL; + st->state=(USE_UDP(st)?QS_UDPINITIAL:QS_TCPINITIAL); + /* fall through */ + } + QS_QUERY_CASES: + tryagain: + rv=p_query_sm(st); + if (rv==-1) { + return -1; + } + if (rv!=RC_OK) { + pdnsd_free(st->msg); + pdnsd_free(st->recvbuf); + st->state=QS_DONE; + if(st->needs_testing) { + switch(st->s_errno) { + case ENETUNREACH: /* network unreachable */ + case EHOSTUNREACH: /* host unreachable */ + case ENOPROTOOPT: /* protocol unreachable */ + case ECONNREFUSED: /* port unreachable */ + case ENETDOWN: /* network down */ + case EHOSTDOWN: /* host down */ +#ifdef ENONET + case ENONET: /* machine not on the network */ +#endif + /* Mark this server as down for a period of time */ + sched_server_test(PDNSD_A(st),1,0); + st->needs_testing=0; + } + } + return rv; + } + /* rv==RC_OK */ + DEBUG_PDNSDA_MSG("Received reply from %s (msg len=%u).\n", PDNSDA2STR(PDNSD_A(st)), st->recvl); + DEBUG_DUMP_DNS_MSG(st->recvbuf, st->recvl); + + /* Basic sanity checks */ + if (st->recvlrecvbuf->id); + if (recvid!=st->myrid) { + DEBUG_MSG("ID mismatch: expected %04x, got %04x!\n", st->myrid, recvid); + goto discard_reply; + } + } + if (st->recvbuf->qr!=QR_RESP) { + DEBUG_MSG("The QR bit indicates this is a query, not a response!\n"); + goto discard_reply; + } + if (st->recvbuf->opcode!=OP_QUERY) { + DEBUG_MSG("Not a reply to a standard query (opcode=%u).\n",st->recvbuf->opcode); + goto discard_reply; + } + + rcode=st->recvbuf->rcode; +#if DEBUG>0 + { + char flgsbuf[DNSFLAGSMAXSTRSIZE]; + DEBUG_MSG("rcode=%u (%s), flags:%s\n", rcode, get_ename(rcode), dnsflags2str(st->recvbuf, flgsbuf)); + } +#endif + if (st->recvbuf->z!=0) { + DEBUG_MSG("Malformed response (nonzero Z bit).\n"); + goto discard_reply; + } + + if(st->needs_testing) { + /* We got an answer from this server, so don't bother with up tests for a while. */ + sched_server_test(PDNSD_A(st),1,1); + st->needs_testing=0; + } + + rv=rcode; + if(rcode==RC_OK || rcode==RC_NAMEERR) { + /* success or at least no requery is needed */ + st->state=QS_DONE; + break; + } + else if (entp) { + if(rcode==RC_SERVFAIL || rcode==RC_NOTSUPP || rcode==RC_REFUSED) { + if (st->msg->hdr.rd && !st->recvbuf->ra) { + /* seems as if we have got no recursion available. + We will have to do it by ourselves (sigh...) */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s." + " Maybe does not support recursive query?" + " Querying non-recursively.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + st->msg->hdr.rd=0; + goto resetstate_tryagain; + } + else if(rcode!=RC_SERVFAIL && st->edns_query && st->msg->hdr.arcount) + goto try_withoutedns; + else if (st->recvbuf->ancount && st->auth_serv==2) { + /* The name server returned a failure code, + but the answer section is not empty, + and the answer is from a server lower down the call chain. + Use this answer tentatively (it may be the + best we can get), but remember the failure. */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s," + " but the answer section is not empty." + " Using the answer tentatively.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + st->failed=3; + st->state=QS_DONE; + break; + } + } + else if(rcode==RC_FORMAT && st->edns_query && st->msg->hdr.arcount) + try_withoutedns: { + size_t transl; + /* Perhaps the remote server barfs when the query + contains an OPT RR in the additional section. + Try again with an empty addtional section. */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s." + " Maybe cannot handle EDNS?" + " Querying with empty additional section.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + transl=remove_opt_pseudo_rr(st->msg,st->transl); + if(transl!=0 && st->msg->hdr.arcount==0) { + st->transl=transl; +#ifndef NO_TCP_QUERIES + st->msg->len=htons(st->transl); +#endif + st->edns_query=0; + resetstate_tryagain: + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->state=(USE_UDP(st)?QS_UDPINITIAL:QS_TCPINITIAL); + goto tryagain; + } + else { + DEBUG_PDNSDA_MSG("Internal error: could not remove additional section from query" + " to server %s\n", PDNSDA2STR(PDNSD_A(st))); + } + } + } + + discard_reply: + /* report failure */ + pdnsd_free(st->msg); + pdnsd_free(st->recvbuf); + /*close(st->sock);*/ + st->state=QS_DONE; +#if DEBUG>0 + if(entp) { + DEBUG_PDNSDA_MSG("Discarding reply from server %s\n", PDNSDA2STR(PDNSD_A(st))); + } +#endif + if (rv!=RC_OK) + return rv; + + return RC_SERVFAIL; /* mock error code */ + + default: /* we shouldn't get here */ + st->state=QS_DONE; + return RC_SERVFAIL; /* mock error code */ + } + + /* If we reach this code, we have successfully received an answer, + * because we have returned error codes on errors or -1 on AGAIN conditions. + * So we *should* have a usable dns record in recvbuf by now. + */ + rd= st->msg->hdr.rd; /* Save the 'Recursion Desired' bit of the query. */ + pdnsd_free(st->msg); + if(entp) { + time_t queryts=time(NULL); + size_t lcnt= ((size_t)st->recvl) - sizeof(dns_hdr_t); + unsigned char *rrp=(unsigned char *)(st->recvbuf+1); + dns_cent_array secs[3]={NULL,NULL,NULL}; +# define ans_sec secs[0] +# define auth_sec secs[1] +# define add_sec secs[2] + unsigned short qtype,flags,aa,neg_ans=0,reject_ans=0,num_ns=0; + int numoptrr; + edns_info_t ednsinfo= {0}; + + if (ntohs(st->recvbuf->qdcount)!=1) { + DEBUG_PDNSDA_MSG("Bad number of query records in answer from %s\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + /* check & skip the query record. */ + { + unsigned char nbuf[DNSNAMEBUFSIZE]; + if ((rv=decompress_name((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, nbuf, NULL))!=RC_OK) { + DEBUG_PDNSDA_MSG("Cannot decompress QNAME in answer from %s\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + if(!rhnicmp(nbuf,name)) { + DEBUG_PDNSDA_MSG("Answer from %s does not match query.\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + } + + qtype=(st->lean_query?thint:QT_ALL); + if (lcnt<4) { + DEBUG_PDNSDA_MSG("Format error in reply from %s (message truncated in qtype or qclass).\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; /* mock error code */ + goto free_recvbuf_return; + } + { + unsigned short qt,qc; + GETINT16(qt,rrp); + GETINT16(qc,rrp); + if(qt!=qtype) { + DEBUG_PDNSDA_MSG("qtype in answer (%u) from %s does not match expected qtype (%u).\n", + qt,PDNSDA2STR(PDNSD_A(st)),qtype); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + } + lcnt-=4; + + st->aa= (st->recvbuf->aa && !st->failed); + st->tc= st->recvbuf->tc; + st->ra= (rd && st->recvbuf->ra); + + /* Don't flag cache entries from a truncated reply as authoritative. */ + aa= (st->aa && !st->tc); + flags=st->flags; + if (aa) flags|=CF_AUTH; + + + /* Initialize a dns_cent_t in the array for the answer section */ + if (!(ans_sec=DA_GROW1(ans_sec))) { + rv=RC_FATALERR; /* unrecoverable error */ + goto free_recvbuf_return; + } + /* By marking DF_AUTH, we mean authoritative AND complete. */ + if (!init_cent(&DA_INDEX(ans_sec,0), name, 0, 0, (aa && qtype==QT_ALL)?DF_AUTH:0 DBG1)) { + rv=RC_FATALERR; /* unrecoverable error */ + goto free_centarrays_recvbuf_return; + } + + /* Now read the answer, authority and additional sections, + storing the results in the arrays ans_sec,auth_sec and add_sec. + */ + numoptrr=0; + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, ntohs(st->recvbuf->ancount), + flags, queryts, &ans_sec, &numoptrr, &ednsinfo); +#if DEBUG>0 + if(numoptrr!=0) { + DEBUG_MSG("Answer section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + numoptrr=0; + if(rv==RC_OK) { + uint16_t nscount=ntohs(st->recvbuf->nscount); + if (nscount) { + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, nscount, + flags|CF_ADDITIONAL, queryts, &auth_sec, &numoptrr, &ednsinfo); +#if DEBUG>0 + if(numoptrr!=0) { + DEBUG_MSG("Authority section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + } + } + + numoptrr=0; + if(rv==RC_OK) { + uint16_t arcount=ntohs(st->recvbuf->arcount); + if (arcount) { + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, arcount, + flags|CF_ADDITIONAL, queryts, &add_sec, &numoptrr, &ednsinfo); + if(numoptrr!=0) { +#if DEBUG>0 + if(numoptrr!=1) { + DEBUG_MSG("Additional section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } + DEBUG_PDNSDA_MSG("Reply from %s contains OPT pseudosection: EDNS version = %u, udp size = %u, flag DO=%u\n", + PDNSDA2STR(PDNSD_A(st)), ednsinfo.version, ednsinfo.udpsize, ednsinfo.do_flg); +#endif + if(rcode!=ednsinfo.rcode) { + DEBUG_PDNSDA_MSG("Reply from %s contains unexpected EDNS rcode %u (%s)!\n", + PDNSDA2STR(PDNSD_A(st)), ednsinfo.rcode, get_ename(ednsinfo.rcode)); + rcode=ednsinfo.rcode; + /* Mark as failed, but use answer tentatively. */ + if(!st->failed) st->failed=1; + } + } + } + } + + if(!(rv==RC_OK || (rv==RC_TRUNC && st->recvbuf->tc))) { + DEBUG_PDNSDA_MSG(rv==RC_FORMAT?"Format error in reply from %s.\n": + rv==RC_TRUNC?"Format error in reply from %s (message unexpectedly truncated).\n": + rv==RC_SERVFAIL?"Inconsistent timestamps in reply from %s.\n": + "Out of memory while processing reply from %s.\n", + PDNSDA2STR(PDNSD_A(st))); + if(rv==RC_SERVFAIL) { + /* Inconsistent ttl timestamps and we are + enforcing strict RFC 2181 compliance. + Mark as failed, but use answer tentatively. */ + if(!st->failed) st->failed=1; + } + else { + if(rv!=RC_FATALERR) rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + + { + /* Remember references to NS and SOA records in the answer or authority section + so that we can add this information to our own reply. */ + int i,n=DA_NEL(ans_sec); + for(i=0;iqname); + + if(getrrset_NS(cent)) + cent->c_ns=scnt; + if(getrrset_SOA(cent)) + cent->c_soa=scnt; + + if((qtype>=QT_MIN && qtype<=QT_MAX) || + (/* (qtype>=T_MIN && qtype<=T_MAX) && */ getrrset(cent,qtype)) || + (n==1 && cent->num_rrs==0)) + { + /* Match this name with names in the authority section */ + int j,m=DA_NEL(auth_sec); + for(j=0;jqname,cent->qname, &rem, NULL); + if(rem==0 && + /* Don't accept records for the root domain from name servers + that were not listed in the configuration file. */ + (ml || st->auth_serv!=2)) { + if(getrrset_NS(ce)) { + if(cent->c_ns==cundef || cent->c_nsc_ns=ml; + } + if(getrrset_SOA(ce)) { + if(cent->c_soa==cundef || cent->c_soac_soa=ml; + } + } + } + } + } + } + + /* Check whether the answer contains an IP address that should be rejected. */ + if(have_rejectlist(st)) { + int i; + int na4=nreject_a4(st); + addr4maskpair_t *a4arr=rejectlist_a4(st); +#if ALLOW_LOCAL_AAAA + int na6=nreject_a6(st); + addr6maskpair_t *a6arr=rejectlist_a6(st); +#endif + /* Check addresses in the answer, authority and additional sections. */ + for(i=0;i<3;++i) { + dns_cent_array sec=secs[i]; + int j,nce=DA_NEL(sec); + for(j=0;jrrs; rr; rr=rr->next) { + struct in_addr *a=(struct in_addr *)(rr->data); + int k; + for(k=0;ka,&am->mask)) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE]; char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Rejecting answer from server %s because it contains an A record" + " for \"%s\" with an address in the reject list: %s\n", + PDNSDA2STR(PDNSD_A(st)), + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)), + inet_ntop(AF_INET,a,abuf,sizeof(abuf))); +#endif + reject_ans=1; goto rejectlist_scan_done; + } + } + } + } +#if ALLOW_LOCAL_AAAA + rrset=getrrset_AAAA(cent); + if(rrset && na6) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + struct in6_addr *a=(struct in6_addr *)(rr->data); + int k; + for(k=0;ka,&am->mask)) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE]; char abuf[INET6_ADDRSTRLEN]; + DEBUG_PDNSDA_MSG("Rejecting answer from server %s because it contains an AAAA record" + " for \"%s\" with an address in the reject list: %s\n", + PDNSDA2STR(PDNSD_A(st)), + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)), + inet_ntop(AF_INET6,a,abuf,sizeof(abuf))); +#endif + reject_ans=1; goto rejectlist_scan_done; + } + } + } + } +#endif + } + } + rejectlist_scan_done:; + } + + /* negative caching for domains */ + if (rcode==RC_NAMEERR) { + DEBUG_PDNSDA_MSG("Server %s returned error code: %s\n", PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + name_error: + neg_ans=1; + { + /* We did not get what we wanted. Cache according to policy */ + dns_cent_t *ent=&DA_INDEX(ans_sec,0); + int neg_domain_pol=global.neg_domain_pol; + if (neg_domain_pol==C_ON || (neg_domain_pol==C_AUTH && st->aa)) { + time_t ttl=global.neg_ttl; + + /* Try to find a SOA record that came with the reply. + */ + if(ent->c_soa!=cundef) { + unsigned scnt=rhnsegcnt(name); + dns_cent_t *cent; + if(ent->c_soac_soa)))) { + rr_set_t *rrset=getrrset_SOA(cent); + if (rrset && rrset->rrs) { + time_t min=soa_minimum(rrset->rrs); + ttl=rrset->ttl; + if(ttl>min) + ttl=min; + } + } + } + DEBUG_RHN_MSG("Caching domain %s negative with ttl %li\n",RHN2STR(name),(long)ttl); + negate_cent(ent,ttl,queryts); + if(st->nocache) ent->flags |= DF_NOCACHE; + goto cleanup_return_OK; + } else { + if(c_soa) *c_soa=ent->c_soa; + free_cent(ent DBG1); + rv=RC_NAMEERR; + goto add_additional; + } + } + } + + if(reject_ans) { + if(reject_policy(st)==C_NEGATE && st->failed<=1) + goto name_error; + else { + rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + + if(global.deleg_only_zones && st->auth_serv<3) { /* st->auth_serv==3 means this server is a root-server. */ + int missingdelegation,authcnt; + /* The deleg_only_zones data may change due to runtime reconfiguration, + therefore use locks. */ + lock_server_data(); + missingdelegation=0; authcnt=0; + { + int i,n=DA_NEL(global.deleg_only_zones); unsigned rem,zrem; + for(i=0;iqname,DA_INDEX(global.deleg_only_zones,l),&rem,&zrem) && zrem==0) { + if(rem) break; + else goto try_next_auth; + } + } + goto delegation_OK; + } + try_next_auth:; + } + } +#if DEBUG>0 + { + unsigned char nmbuf[DNSNAMEBUFSIZE],zbuf[DNSNAMEBUFSIZE]; + DEBUG_PDNSDA_MSG(authcnt?"%s is in %s zone, but no delegation found in answer returned by server %s\n" + :"%s is in %s zone, but no authority information provided by server %s\n", + rhn2str(name,nmbuf,sizeof(nmbuf)), rhn2str(DA_INDEX(global.deleg_only_zones,i),zbuf,sizeof(zbuf)), + PDNSDA2STR(PDNSD_A(st))); + } +#endif + missingdelegation=1; + } + delegation_OK:; + } + unlock_server_data(); + + if(missingdelegation) { + if(authcnt && st->failed<=1) { + /* Treat this as a nonexistant name. */ + goto name_error; + } + else if(st->auth_serv<2) { + /* If this is one of the servers obtained from the list + pdnsd was configured with, treat this as a failure. + Hopefully one of the other servers in the list will + return a non-empty authority section. + */ + rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + } + + { + /* Negative caching of rr sets */ + dns_cent_t *ent=&DA_INDEX(ans_sec,0); + + if(!ent->num_rrs) neg_ans=1; + + if (thint>=T_MIN && thint<=T_MAX && !getrrset(ent,thint) && !st->tc && st->failed<=1) { + /* We did not get what we wanted. Cache according to policy */ + int neg_rrs_pol=global.neg_rrs_pol; + if (neg_rrs_pol==C_ON || (neg_rrs_pol==C_AUTH && aa) || + (neg_rrs_pol==C_DEFAULT && (aa || st->ra))) + { + time_t ttl=global.neg_ttl; + rr_set_t *rrset=getrrset_SOA(ent); + dns_cent_t *cent; + unsigned scnt; + /* If we received a SOA, we should take the ttl of that record. */ + if ((rrset && rrset->rrs) || + /* Try to find a SOA record higher up the hierarchy that came with the reply. */ + ((cent=lookup_cent_array(auth_sec, + (ent->c_soa!=cundef && ent->c_soa<(scnt=rhnsegcnt(name)))? + skipsegs(name,scnt-ent->c_soa): + name)) && + (rrset=getrrset_SOA(cent)) && rrset->rrs)) + { + time_t min=soa_minimum(rrset->rrs); + ttl=rrset->ttl; + if(ttl>min) + ttl=min; + } + DEBUG_RHN_MSG("Caching type %s for domain %s negative with ttl %li\n",getrrtpname(thint),RHN2STR(name),(long)ttl); + if (!add_cent_rrset_by_type(ent, thint, ttl, queryts, CF_NEGATIVE|flags DBG1)) { + rv=RC_FATALERR; + goto free_ent_centarrays_recvbuf_return; + } + } + } + } + + if (st->failed<=1) { + /* The domain names of all name servers found in the answer and authority sections are placed in *ns, + which is automatically grown. */ + /* dns_cent_array secs[2]={ans_sec,auth_sec}; */ + int i; + for(i=0;i<2;++i) { + dns_cent_array sec=secs[i]; + int j,n=DA_NEL(sec); + for(j=0;jqname) || st->auth_serv!=2) && + /* Don't accept possibly poisoning nameserver entries in paranoid mode */ + (st->trusted || !st->nsdomain || (domain_match(st->nsdomain, cent->qname, &rem,NULL),rem==0)) && + /* The following test is actually redundant and should never fail. */ + *(cent->qname)!=0xff) + { + /* Some nameservers obviously choose to send SOA records instead of NS ones. + * Although I think that this is poor behaviour, we'll have to work around that. */ + static const unsigned short nstypes[2]={T_NS,T_SOA}; + int k; + for(k=0;k<2;++k) { + rr_set_t *rrset=getrrset(cent,nstypes[k]); + if(rrset) { + rr_bucket_t *rr; + unsigned short first=1; + for(rr=rrset->rrs; rr; rr=rr->next) { + size_t sz1,sz2; + unsigned char *p; + /* Skip duplicate records */ + for(p=dlist_first(*ns); p; p=dlist_next(p)) { + if(rhnicmp(*p==0xff?p+1:skiprhn(p),(unsigned char *)(rr->data))) + goto next_nsr; + } + /* add to the nameserver list. + Here we use a little compression trick: if + the first byte of a name is 0xff, this means + repeat the previous name. + */ + sz1= (first?rhnlen(cent->qname):1); + sz2=rhnlen((unsigned char *)(rr->data)); + if (!(*ns=dlist_grow(*ns,sz1+sz2))) { + rv=RC_FATALERR; + goto free_ent_centarrays_recvbuf_return; + } + p=dlist_last(*ns); + if(first) { + first=0; + p=mempcpy(p,cent->qname,sz1); + } + else + *p++ = 0xff; /* 0xff means 'idem' */ + /* This will only copy the first name, which is the NS */ + memcpy(p,(unsigned char *)(rr->data),sz2); + ++num_ns; + next_nsr:; + } + } + } + } + } + } + } + cleanup_return_OK: + if(st->failed && neg_ans && num_ns==0) { + DEBUG_PDNSDA_MSG("Answer from server %s does not contain usable records.\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_ns_ent_centarrays_recvbuf_return; + } + if(!(*entp=malloc(sizeof(dns_cent_t)))) { + rv=RC_FATALERR; + goto free_ns_ent_centarrays_recvbuf_return; + } + **entp=DA_INDEX(ans_sec,0); + rv=RC_OK; + add_additional: + if (!st->failed && !reject_ans) { + /* Add the additional RRs to the cache. */ + /* dns_cent_array secs[3]={ans_sec,auth_sec,add_sec}; */ + int i; +#if DEBUG>0 + if(debug_p && neg_ans) { + int j,n=DA_NEL(ans_sec); + for(j=1; jqname) || st->auth_serv!=2) { + unsigned int rem; + if(st->trusted || !st->nsdomain || (domain_match(st->nsdomain, cent->qname, &rem, NULL),rem==0)) + add_cache(cent); + else { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE],nsbuf[DNSNAMEBUFSIZE]; + DEBUG_MSG("Record for %s not in nsdomain %s; dropped.\n", + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)),rhn2str(st->nsdomain,nsbuf,sizeof(nsbuf))); +#endif + } + } + else { +#if DEBUG>0 + static const char *const secname[3]={"answer","authority","additional"}; + DEBUG_PDNSDA_MSG("Record(s) for root domain in %s section from %s dropped.\n", secname[i],PDNSDA2STR(PDNSD_A(st))); +#endif + } + } + } + } + goto free_centarrays_recvbuf_return; + + free_ns_ent_centarrays_recvbuf_return: + dlist_free(*ns); *ns=NULL; + free_ent_centarrays_recvbuf_return: + if(DA_NEL(ans_sec)>=1) free_cent(&DA_INDEX(ans_sec,0) DBG1); + free_centarrays_recvbuf_return: + { + /* dns_cent_array secs[3]={ans_sec,auth_sec,add_sec}; */ + int i; + for(i=0;i<3;++i) { + dns_cent_array sec=secs[i]; + int j,n=DA_NEL(sec); + /* The first entry in the answer section is treated separately, so skip that one. */ + for(j= !i; jrecvbuf); + return rv; +} + +/* + * Cancel a query, freeing all resources. Any query state is valid as input (this may even be called + * if a call to p_exec_query already returned error or success) + */ +static void p_cancel_query(query_stat_t *st) +{ + switch (st->state) { + QS_WRITE_CASES: + QS_READ_CASES: + close(st->sock); + /* fall through */ + case QS_TCPINITIAL: + case QS_UDPINITIAL: + pdnsd_free(st->recvbuf); + pdnsd_free(st->msg); + } + if(st->state!=QS_INITIAL && st->state!=QS_DONE) + st->state=QS_CANCELED; +} + +#if 0 +/* + * Initialize a query_serv_t (server list for parallel query) + * This is there for historical reasons only. + */ +inline static void init_qserv(query_stat_array *q) +{ + *q=NULL; +} +#endif + +/* + * Add a server entry to a query_serv_t + * Note: only a reference to nsdomain is copied, not the name itself. + * Be sure to free the q-list before freeing the name. + */ +static int add_qserv(query_stat_array *q, pdnsd_a2 *a, int port, time_t timeout, unsigned flags, + char nocache, char lean_query, char edns_query, char auth_s, char needs_testing, char trusted, + const unsigned char *nsdomain, rejectlist_t *rejectlist) +{ + query_stat_t *qs; + + if ((*q=DA_GROW1(*q))==NULL) { + DEBUG_MSG("Out of memory in add_qserv()\n"); + return 0; + } + + qs=&DA_LAST(*q); +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&qs->a.sin4,0,sizeof(qs->a.sin4)); + qs->a.sin4.sin_family=AF_INET; + qs->a.sin4.sin_port=htons(port); + qs->a.sin4.sin_addr=a->ipv4; + SET_SOCKA_LEN4(qs->a.sin4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&qs->a.sin6,0,sizeof(qs->a.sin6)); + qs->a.sin6.sin6_family=AF_INET6; + qs->a.sin6.sin6_port=htons(port); + qs->a.sin6.sin6_flowinfo=IPV6_FLOWINFO; + qs->a.sin6.sin6_addr=a->ipv6; + SET_SOCKA_LEN6(qs->a.sin6); + + qs->a4fallback=a->ipv4; + } +#endif + qs->timeout=timeout; + qs->flags=flags; + qs->nocache=nocache; + qs->auth_serv=auth_s; + qs->lean_query=lean_query; + qs->edns_query=edns_query; + qs->needs_testing=needs_testing; + qs->trusted=trusted; + qs->aa=0; + qs->tc=0; + qs->ra=0; + qs->failed=0; + qs->nsdomain=nsdomain; /* Note: only a reference is copied, not the name itself! */ + qs->rejectlist=rejectlist; + + qs->state=QS_INITIAL; + qs->qm=global.query_method; + qs->s_errno=0; + return 1; +} + +/* Test whether two pdnsd_a2 addresses are the same. */ +inline __attribute__((always_inline)) +static int same_inaddr2_2(pdnsd_a2 *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) && + a->ipv4.s_addr==b->ipv4.s_addr ); +} + +/* This can be used to check whether a server address was already used in a + previous query_stat_t entry. */ +inline static int query_stat_same_inaddr2(query_stat_t *qs, pdnsd_a2 *b) +{ + return SEL_IPVER( qs->a.sin4.sin_addr.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&qs->a.sin6.sin6_addr,&b->ipv6) && + qs->a4fallback.s_addr==b->ipv4.s_addr ); +} + + +/* + * Free resources used by a query_serv_t + * There for historical reasons only. + */ +inline static void del_qserv(query_stat_array q) +{ + da_free(q); +} + +struct qstatnode_s { + query_stat_array qa; + struct qstatnode_s *next; +}; +typedef struct qstatnode_s qstatnode_t; + +struct qhintnode_s { + const unsigned char *nm; + int tp; + struct qhintnode_s *next; +}; +/* typedef struct qhintnode_s qhintnode_t; */ /* Already defined in dns_query.h */ + +static int auth_ok(query_stat_array q, const unsigned char *name, int thint, dns_cent_t *ent, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + query_stat_t *qse, dlist ns, query_stat_array *serv); +static int p_dns_cached_resolve(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa); +static int simple_dns_cached_resolve(atup_array atup_a, int port, char edns_query, time_t timeout, + const unsigned char *name, int thint, dns_cent_t **cachedp); + + +/* + * Performs a semi-parallel query on the servers in q. PAR_QUERIES are executed parallel at a time. + * name is the query name in dns protocol format (number.string etc), + * ent is the dns_cent_t that will be filled. + * hops is the number of recursions left. + * qslist should refer to a list of server arrays used higher up in the calling chain. This way we can + * avoid name servers that have already been tried for this name. + * qhlist should refer to a list of names that we are trying to resolve higher up in the calling chain. + * These names should be avoided further down the chain, or we risk getting caught in a wasteful cycle. + * thint is a hint on the requested query type used to decide whether an aa record must be fetched + * or a non-authoritative answer will be enough. + * + * nocache is needed because we add AA records to the cache. If the nocache flag is set, we do not + * take the original values for the record, but flags=0 and ttl=0 (but only if we do not already have + * a cached record for that set). These settings cause the record be purged on the next cache addition. + * It will also not be used again. + * + * The return value of p_recursive_query() has the same meaning as that of p_dns_cached_resolve() + * (see below). + */ +static int p_recursive_query(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **entp, + int *nocache, int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + unsigned char *c_soa) +{ + dns_cent_t *ent,*entsave=NULL; + int i,j,k; + int rv=RC_SERVFAIL; + int qualval=0; + query_stat_t *qse=NULL; /* Initialized to inhibit compiler warning */ + dlist ns=NULL,nssave=NULL; + query_stat_array serv=NULL,servsave=NULL; + +# define W_AUTHOK 8 +# define W_NOTFAILED 2 +# define W_NOTTRUNC 1 +# define NOTFAILMASK 6 +# define GOODQUAL (W_AUTHOK+3*W_NOTFAILED) +# define save_query_result(ent,qs,ns,serv,authok) \ + { \ + int qval = authok*W_AUTHOK + (3-qs->failed)*W_NOTFAILED + (!qs->tc)*W_NOTTRUNC; \ + if(entsave && qval>qualval) { \ + /* Free the old copy, because the new result is better. */ \ + free_cent(entsave DBG1); \ + pdnsd_free(entsave); \ + entsave=NULL; \ + del_qserv(servsave); \ + dlist_free(nssave); \ + } \ + if(!entsave) { \ + entsave=ent; \ + servsave=serv; \ + /* The serv array contains references to data within the ns list, \ + so we need to save a copy of the ns list as well! */ \ + if(DA_NEL(serv)>0) nssave=ns; else {nssave=NULL;dlist_free(ns);} \ + qualval=qval; \ + qse=qs; \ + } \ + else { \ + /* We already have a copy, free the present one. */ \ + free_cent(ent DBG1); \ + pdnsd_free(ent); \ + del_qserv(serv); \ + dlist_free(ns); \ + } \ + serv=NULL; \ + ns=NULL; \ + } + + { + time_t ts0=time(NULL),global_timeout=global.timeout; + int dc=0,mc=0,nq=DA_NEL(q),parqueries=global.par_queries; + + for (j=0; jnq) mc=nq; + + /* First, call p_exec_query once for each parallel set to initialize. + * Then, as long as not all have the state QS_DONE or we have a timeout, + * build a poll/select set for all active queries and call them accordingly. */ + for (i=dc;i=j) { + /* The below should not happen any more, but may once again + * (immediate success) */ + DEBUG_PDNSDA_MSG("Sending query to %s\n", PDNSDA2STR(PDNSD_A(qs))); + retryquery: + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + && !(qs->qm==UDP_TCP && qs->tc) +#endif + ) + { + qse=qs; + mc=i; /* No need to cancel queries beyond i */ + goto done; + } + } + else { + mc=i; /* No need to cancel queries beyond i */ + goto free_ent_return_failed; + } + } + /* We do not have a satisfactory answer. + However, we will save a copy in case none of the other + servers in the q list give a satisfactory answer either. + */ + save_query_result(ent,qs,ns,serv,authok); +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(qs->qm==UDP_TCP && qs->tc) { + switch_to_tcp(qs); + DEBUG_PDNSDA_MSG("Reply from %s was truncated. Trying again using TCP.\n", + PDNSDA2STR(PDNSD_A(qs))); + goto retryquery; + } +#endif + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + mc=i; /* No need to cancel queries beyond i */ + goto done; + } + } + if (qs->state==QS_DONE && i==dc) + dc++; + } + if (dcstate!=QS_DONE) { + if (i>=j && qs->timeout>maxto) + maxto=qs->timeout; +#ifdef NO_POLL + if (qs->sock>maxfd) { + maxfd=qs->sock; + PDNSD_ASSERT(maxfdstate) { + QS_READ_CASES: + FD_SET(qs->sock,&reads); + break; + QS_WRITE_CASES: + FD_SET(qs->sock,&writes); + break; + } +#else + polls[pc].fd=qs->sock; + switch (qs->state) { + QS_READ_CASES: + polls[pc].events=POLLIN; + break; + QS_WRITE_CASES: + polls[pc].events=POLLOUT; + break; + default: + polls[pc].events=0; + } +#endif + pc++; + } + } + if (pc==0) { + /* In this case, ALL are done and we do not need to cancel any + * query. */ + dc=mc; + break; + } + now=time(NULL); + maxto -= now-ts; + if (mc==nq) { +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + /* Don't use the global timeout if there are TCP queries + we might want to retry using UDP. */ + for (i=j;imaxto) maxto=globto; + } +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + skip_globto:; +#endif + } +#ifdef NO_POLL + tv.tv_sec=(maxto>0)?maxto:0; + tv.tv_usec=0; + nevents=select(maxfd+1,&reads,&writes,NULL,&tv); +#else + nevents=poll(polls,pc,(maxto>0)?(maxto*1000):0); +#endif + if (nevents<0) { + /* if(errno==EINTR) + continue; */ + log_warn("poll/select failed: %s",strerror(errno)); + goto done; + } + if (nevents==0) { + /* We have timed out. Mark the unresponsive servers so that we can consider + them for retesting later on. We will continue to listen for replies from + these servers as long as we have additional servers to try. */ + for (i=j;istate!=QS_DONE && qs->needs_testing) + qs->needs_testing=2; +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if (tentative_tcp_query(qs)) { + /* We timed out while waiting for a TCP connection. + Try again using UDP. + */ + close(qs->sock); + switch_to_udp(qs); + DEBUG_PDNSDA_MSG("TCP connection to %s timed out. Trying to use UDP.\n", + PDNSDA2STR(PDNSD_A(qs))); + + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + /* In the unlikely case of immediate success */ + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed) { + qse=qs; + goto done; + } + } + else + goto free_ent_return_failed; + } + save_query_result(ent,qs,ns,serv,authok); + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + goto done; + } + ++nevents; + } +#endif + } +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if (mc==nq) { + /* We will not try additional servers, but we might want to try again + using UDP instead of TCP + */ + if(nevents && (time(NULL)-ts0)state!=QS_DONE) { + int srv_event=0; + /* This detection may seem suboptimal, but normally, we have at most 2-3 parallel + * queries, and anything else would be higher overhead, */ +#ifdef NO_POLL + switch (qs->state) { + QS_READ_CASES: + srv_event=FD_ISSET(qs->sock,&reads); + break; + QS_WRITE_CASES: + srv_event=FD_ISSET(qs->sock,&writes); + break; + } +#else + do { + PDNSD_ASSERT(icsock); + /* + * In case of an error, reenter the state machine + * to catch it. + */ + switch (qs->state) { + QS_READ_CASES: + srv_event=polls[k].revents&(POLLIN|POLLERR|POLLHUP|POLLNVAL); + break; + QS_WRITE_CASES: + srv_event=polls[k].revents&(POLLOUT|POLLERR|POLLHUP|POLLNVAL); + break; + } +#endif + if (srv_event) { + --nevents; + retryquery2: + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + && !(qs->qm==UDP_TCP && qs->tc) +#endif + ) + { + qse=qs; + goto done; + } + } + else + goto free_ent_return_failed; + } + save_query_result(ent,qs,ns,serv,authok); +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(qs->qm==UDP_TCP && qs->tc) { + switch_to_tcp(qs); + DEBUG_PDNSDA_MSG("Reply from %s was truncated. Trying again using TCP.\n", + PDNSDA2STR(PDNSD_A(qs))); + goto retryquery2; + } +#endif + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + goto done; + } + } + } + /* recheck, this might have changed after the last p_exec_query */ + if (qs->state==QS_DONE && i==dc) + dc++; + } + if(nevents>0) { + /* We have not managed to handle all the events reported by poll/select. + Better call it quits, or we risk getting caught in a wasteful cycle. + */ + if(++poll_errs<=MAXPOLLERRS) + log_error("%d unhandled poll/select event(s) in p_recursive_query() at %s, line %d.",nevents,__FILE__,__LINE__); + rv=RC_SERVFAIL; + goto done; + } + } while (dc 1) + ++n; + if(n>0) { + pdnsd_a addrs[n]; /* variable length array */ + k=0; + for (i=0;ineeds_testing > 1) + addrs[k++]= *PDNSD_A(qs); + } + sched_server_test(addrs,n,-1); + } + } + } + + if(entsave) { + /* + * If we didn't get rrs from any of the authoritative servers, or the answers were + * unsatisfactory for another reason, take the one we had. + * However, raise the CF_NOCACHE flag, so that it won't be used again (outside the + * cache latency period). + */ + DEBUG_PDNSDA_MSG("Using %s reply from %s.\n", + !(qualval&NOTFAILMASK)? "reportedly failed": + !(qualval&W_NOTFAILED)? "inconsistent": + !(qualval&W_NOTTRUNC)? "truncated": + !(qualval&W_AUTHOK)? "non-authoritative": "good", + PDNSDA2STR(PDNSD_A(qse))); + ent=entsave; + serv=servsave; + ns=nssave; + if(qualvalflags&DF_NEGATIVE)) { + int jlim= RRARR_LEN(ent); + for (j=0; jflags |= CF_NOCACHE; + } + } + else /* Very unlikely, but not impossible. */ + ent->flags |= DF_NOCACHE; + } + rv=RC_OK; + } + else if (rv!=RC_OK) { + if(rv==RC_FATALERR) { + DEBUG_MSG("Unrecoverable error encountered while processing query.\n"); + rv=RC_SERVFAIL; + } + DEBUG_MSG("%sReturning error code \"%s\"\n", + rv!=RC_NAMEERR? "No query succeeded. ": "", + get_ename(rv)); + goto clean_up_return; + } + + if(nocache) *nocache=qse->nocache; + + if (DA_NEL(serv)>0) { + /* Authority records present. Ask them, because the answer was non-authoritative. */ + qstatnode_t qsn={q,qslist}; + unsigned char save_ns=ent->c_ns,save_soa=ent->c_soa; + + if(qse->aa || qse->ra) { + /* The server claimed to be authoritative or have recursion available, + yet we did not completely trust the answer for some reason. + We will try to ask the servers in the authority records, + but in case we fail, we will save a copy of the answer. */ + entsave=ent; + } + else { + free_cent(ent DBG1); + pdnsd_free(ent); + entsave=NULL; + } + rv=p_dns_cached_resolve(serv, name, thint,&ent,hops-1,&qsn,qhlist,time(NULL),c_soa); + if(rv==RC_OK || rv==RC_CACHED || (rv==RC_STALE && !entsave)) { + if(save_ns!=cundef && (ent->c_ns==cundef || ent->c_nsc_ns=save_ns; + if(save_soa!=cundef && (ent->c_soa==cundef || ent->c_soac_soa=save_soa; + goto free_entsave; + } + else if(rv==RC_NAMEERR) { + if(c_soa && save_soa!=cundef && (*c_soa==cundef || *c_soaaa? "be authoritative": "have recursion available"); + ent=entsave; + rv=RC_OK; + } + } + + clean_up_return: + /* Always free the serv array before freeing the ns list, + because the serv array contains references to data within the ns list! */ + del_qserv(serv); + dlist_free(ns); + + if(rv==RC_OK || rv==RC_CACHED || rv==RC_STALE) *entp=ent; + return rv; +# undef save_query_result +} + +/* auth_ok returns 1 if we don't need an authoritative answer or + if we can find servers to ask for an authoritative answer. + In the latter case these servers will be added to the *serv list. + A return value of 0 means the answer is not satisfactory in the + previous sense. + A return value of -1 indicates an error. +*/ +static int auth_ok(query_stat_array q, const unsigned char *name, int thint, dns_cent_t *ent, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + query_stat_t *qse, dlist ns, query_stat_array *serv) +{ + int retval=0; + + /* If the answer was obtained from a name server which returned a failure code, + the answer is never satisfactory. */ + if(qse->failed > 1) return 0; + + /* + Look into the query type hint. If it is a wildcard (QT_*), we need an authoritative answer. + Same if there is no record that answers the query. + This test will also succeed if we have a negative cached record. This is done purposely. + */ +#define aa_needed ((thint>=QT_MIN && thint<=QT_MAX) || \ + ((thint>=T_MIN && thint<=T_MAX) && \ + (!have_rr(ent,thint) && !have_rr_CNAME(ent)))) + + /* We will want to query authoritative servers if all of the following conditions apply: + + 1) The server from which we got the answer was not configured as "proxy only". + 2) The answer is not a negatively cached domain (i.e. the server did not reply with NXDOMAIN). + 3) The query type is a wild card (QT_*), or no record answers the query. + 4) The answer that we have is non-authoritative. + */ + if(!(qse->auth_serv && !(ent->flags&DF_NEGATIVE) && aa_needed)) + return 1; + + if(qse->aa) { + /* The reply we have claims to be authoritative. + However, I have seen cases where name servers raise the authority flag incorrectly (groan...), + so as a work-around, we will check whether the domains for which the servers in the ns + list are responsible, match the queried name better than the domain for which the + last server was responsible. */ + unsigned char *nsdomain; + + if(!qse->nsdomain) + return 1; + + nsdomain=dlist_first(ns); + if(!nsdomain) + return 1; + for(;;) { + unsigned int rem,crem; + domain_match(nsdomain,qse->nsdomain,&rem,&crem); + if(!(rem>0 && crem==0)) + return 1; + domain_match(nsdomain,name,&rem,NULL); + if(rem!=0) + return 1; + do { + nsdomain=dlist_next(nsdomain); + if(!nsdomain) + goto done_checkauth; + } while(*nsdomain==0xff); /* Skip repeats. */ + } + done_checkauth:; + + /* The name servers in the ns list are a better match for the queried name than + the server from which we got the last reply, so ignore the aa flag. + */ +#if DEBUG>0 + if(debug_p) { + unsigned char dbuf[DNSNAMEBUFSIZE],sdbuf[DNSNAMEBUFSIZE]; + nsdomain=dlist_first(ns); + DEBUG_PDNSDA_MSG("The name server %s which is responsible for the %s domain, raised the aa flag, but appears to delegate to the sub-domain %s\n", + PDNSDA2STR(PDNSD_A(qse)), + rhn2str(qse->nsdomain,dbuf,sizeof(dbuf)), + rhn2str(nsdomain,sdbuf,sizeof(sdbuf))); + } +#endif + } + + /* The answer was non-authoritative. Try to build a list of addresses of authoritative servers. */ + if (hops>0) { + unsigned char *nsdomp, *nsdomain=NULL; + rr_set_t *localrrset=NULL; + rr_bucket_t *localrr=NULL; + for (nsdomp=dlist_first(ns);;) { + unsigned char *nsname=NULL; /* Initialize to inhibit compiler warning. */ + int nserva, ia, n; + pdnsd_a2 serva[MAXNAMESERVIPS]; + + /* Get next name server. */ + if(localrr) { + /* Use next locally defined NS record. */ + nsname=(unsigned char *)(localrr->data); + localrr= localrr->next; + } + else { + if(localrrset) { + /* clean up rrset */ + del_rrset(localrrset DBG1); + localrrset=NULL; + } + if(!nsdomp) + break; + else if(*nsdomp!=0xff) { + /* New domain. */ + nsdomain=nsdomp; + if (global.paranoid) { + unsigned int rem; + /* paranoia mode: don't query name servers that are not responsible */ + domain_match(nsdomain,name,&rem,NULL); + if (rem!=0) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE],dbuf[DNSNAMEBUFSIZE],nsbuf[DNSNAMEBUFSIZE]; + DEBUG_MSG("The name server %s is responsible for the %s domain, which does not match %s\n", + rhn2str(nsname,nsbuf,sizeof(nsbuf)), + rhn2str(nsdomain,dbuf,sizeof(dbuf)), + rhn2str(name,nmbuf,sizeof(nmbuf))); +#endif + /* Skip records in ns list for the same domain. */ + do { + nsdomp=dlist_next(nsdomp); + } while (nsdomp && *nsdomp==0xff); + continue; + } + } + /* Check if we have locally defined NS records, because + they will override the ones provided by remote servers. + */ + localrrset=lookup_cache_local_rrset(nsdomain,T_NS); + if(localrrset) { + /* Skip records in ns list for the same domain. */ + do { + nsdomp=dlist_next(nsdomp); + } while (nsdomp && *nsdomp==0xff); + localrr=localrrset->rrs; + if(!localrr) continue; + nsname=(unsigned char *)(localrr->data); + localrr= localrr->next; + } + else { + nsname=skiprhn(nsdomp); + nsdomp=dlist_next(nsdomp); + } + } + else { + /* domain repeated. */ + nsname= nsdomp+1; + nsdomp=dlist_next(nsdomp); + } + } + /* look it up in the cache or resolve it if needed. + The records received should be in the cache now, so it's ok. + */ + nserva=0; + + { + const unsigned char *nm=name; + int tp=thint; + qhintnode_t *ql=qhlist; + + for(;;) { + if(rhnicmp(nm,nsname) && tp==T_A) { + DEBUG_RHN_MSG("Not looking up address for name server \"%s\": " + "risk of infinite recursion.\n",RHN2STR(nsname)); + goto skip_server; + } + if(!ql) break; + nm=ql->nm; + tp=ql->tp; + ql=ql->next; + } + { + qhintnode_t qhn={name,thint,qhlist}; + dns_cent_t *servent; + if (r_dns_cached_resolve(nsname,T_A, &servent, hops-1, &qhn,time(NULL),NULL)==RC_OK) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_bucket_t *rrs6= (rrset6? rrset6->rrs: NULL); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs4= (rrset4? rrset4->rrs: NULL); + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + } + } + +#if DEBUG>0 + if(nserva==0) { + DEBUG_RHN_MSG("Looking up address for name server \"%s\" failed.\n",RHN2STR(nsname)); + } +#endif + n=DA_NEL(*serv); + for(ia=0; iastate==QS_DONE && equiv_inaddr2(PDNSD_A(qs),pserva)) { + DEBUG_PDNSDA_MSG("Not trying name server %s, already queried.\n", PDNSDA2STR(PDNSD_A2_TO_A(pserva))); + goto skip_server_addr; + } + } + if(!ql) break; + qa=ql->qa; + ql=ql->next; + } + } + + /* lean query mode is inherited. CF_AUTH and CF_ADDITIONAL are not (as specified + * in CFF_NOINHERIT). */ + if (!add_qserv(serv, pserva, 53, qse->timeout, qse->flags&~CFF_NOINHERIT, 0, + qse->lean_query,qse->edns_query,2,0,!global.paranoid,nsdomain, + inherit_rejectlist(qse)?qse->rejectlist:NULL)) + { + return -1; + } + retval=1; + skip_server_addr:; + } + skip_server:; + } +#if DEBUG>0 + if(!retval) { + DEBUG_PDNSDA_MSG("No remaining authoritative name servers to try in authority section from %s.\n", PDNSDA2STR(PDNSD_A(qse))); + } +#endif + } + else { + DEBUG_MSG("Maximum hops count reached; not trying any more name servers.\n"); + } + + return retval; + +#undef aa_needed +} + +/* + * This checks the given name to resolve against the access list given for the server using the + * include=, exclude= and policy= parameters. + */ +static int use_server(servparm_t *s, const unsigned char *name) +{ + int i,n=DA_NEL(s->alist); + + for (i=0;ialist,i); + unsigned int nrem,lrem; + domain_match(name,sl->domain,&nrem,&lrem); + if(!lrem && (!sl->exact || !nrem)) + return sl->rule==C_INCLUDED; + } + + if (s->policy==C_SIMPLE_ONLY || s->policy==C_FQDN_ONLY) { + if(rhnsegcnt(name)<=1) + return s->policy==C_SIMPLE_ONLY; + else + return s->policy==C_FQDN_ONLY; + } + + return s->policy==C_INCLUDED; +} + +#if ALLOW_LOCAL_AAAA +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL || (s)->reject_a6!=NULL) +#else +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL) +#endif + +/* Take the lists of IP addresses from a server section sp and + convert them into a form that can be used by p_exec_query(). + If successful, add_rejectlist returns a new list which is added to the old list rl, + otherwise the return value is NULL. +*/ +static rejectlist_t *add_rejectlist(rejectlist_t *rl, servparm_t *sp) +{ + int i,na4=DA_NEL(sp->reject_a4); + addr4maskpair_t *a4p; +#if ALLOW_LOCAL_AAAA + int na6=DA_NEL(sp->reject_a6); + addr6maskpair_t *a6p; +#endif + rejectlist_t *rlist = malloc(sizeof(rejectlist_t) + na4*sizeof(addr4maskpair_t) +#if ALLOW_LOCAL_AAAA + + na6*sizeof(addr6maskpair_t) +#endif + ); + + if(rlist) { +#if ALLOW_LOCAL_AAAA + /* Store the larger IPv6 addresses first to avoid possible alignment problems. */ + rlist->na6 = na6; + a6p = (addr6maskpair_t *)rlist->rdata; + for(i=0;ireject_a6,i); +#endif + rlist->na4 = na4; +#if ALLOW_LOCAL_AAAA + a4p = (addr4maskpair_t *)a6p; +#else + a4p = (addr4maskpair_t *)rlist->rdata; +#endif + for(i=0;ireject_a4,i); + + rlist->policy = sp->rejectpolicy; + rlist->inherit = sp->rejectrecursively; + rlist->next = rl; + } + else { + DEBUG_MSG("Out of memory in add_rejectlist()\n"); + } + + return rlist; +} + +inline static void free_rejectlist(rejectlist_t *rl) +{ + while(rl) { + rejectlist_t *next = rl->next; + free(rl); + rl=next; + } +} + +/* Lookup addresses of nameservers provided by root servers for a given domain in the cache. + Returns NULL if unsuccessful (or the cache entries have timed out). +*/ +static addr2_array lookup_ns(const unsigned char *domain) +{ + addr2_array res=NULL; + + dns_cent_t *cent=lookup_cache(domain,NULL); + if(cent) { + rr_set_t *rrset=getrrset_NS(cent); + if(rrset && (rrset->flags&CF_ROOTSERV) && !timedout(rrset)) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + dns_cent_t *servent=lookup_cache((unsigned char*)(rr->data),NULL); + int nserva=0; + pdnsd_a2 serva[MAXNAMESERVIPS]; + if(servent) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset && !timedout(rrset)) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs6=NULL, *rrs4=NULL; + if (rrset6 && !(rrset6->flags&CF_NEGATIVE)) { + if(!timedout(rrset6)) { + rrs6= rrset6->rrs; + if (rrs6 && rrset4 && !(rrset4->flags&CF_NEGATIVE)) { + if(timedout(rrset4) || !(rrs4=rrset4->rrs)) + /* Treat this as a failure. */ + rrs6=NULL; + } + } + } + else if (rrset4 && !timedout(rrset4)) + rrs4= rrset4->rrs; + + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + if(nserva==0) { + /* Address lookup failed. */ + da_free(res); res=NULL; + break; + } + else { + int i, j, n=DA_NEL(res); + for(i=0; irrs; rr; rr=rr->next) { + dns_cent_t *servent; + int nserva=0; + pdnsd_a2 serva[MAXNAMESERVIPS]; + + rc=simple_dns_cached_resolve(atup_a,port,edns_query,timeout, + (const unsigned char *)(rr->data),T_A,&servent); + if(rc==RC_OK) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_bucket_t *rrs6= (rrset6? rrset6->rrs: NULL); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs4= (rrset4? rrset4->rrs: NULL); + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + else { + DEBUG_RHN_MSG("Simple query for %s type A failed (rc: %s)\n", + RHN2STR((const unsigned char *)(rr->data)),get_ename(rc)); + } + + if(nserva==0) { + /* Address lookup failed. */ + DEBUG_RHN_MSG("Failed to obtain address of root server %s in dns_rootserver_resolv()\n", + RHN2STR((const unsigned char *)(rr->data))); + ++nfail; + } + else { + int i, j, n=DA_NEL(res); + for(i=0; iDA_NEL(res)) { + DEBUG_MSG("Too many root-server resolve failures (%u succeeded, %u failed)," + " rejecting the result.\n", DA_NEL(res),nfail); + da_free(res); res=NULL; + } + } + free_cent_return: + free_cent(cent DBG1); + pdnsd_free(cent); + } + else { + DEBUG_MSG("Simple query for root domain type NS failed (rc: %s)\n",get_ename(rc)); + } + + return res; +} + + +static int p_dns_resolve(const unsigned char *name, int thint, dns_cent_t **cachedp, int hops, qhintnode_t *qhlist, + unsigned char *c_soa) +{ + int i,n,rc; + int one_up=0,seenrootserv=0; + query_stat_array serv=NULL; + rejectlist_t *rejectlist=NULL; + + /* try the servers in the order of their definition */ + lock_server_data(); + n=DA_NEL(servers); + for (i=0;irootserver<=1 && use_server(sp,name)) { + int m=DA_NEL(sp->atup_a); + if(m>0) { + rejectlist_t *rjl=NULL; + int j=0, jstart=0; + if(sp->rand_servers) j=jstart=random()%m; + do { + atup_t *at=&DA_INDEX(sp->atup_a,j); + if (at->is_up) { + if(sp->rootserver) { + if(!seenrootserv) { + int nseg,mseg=1,l=0; + const unsigned char *topdomain=NULL; + addr2_array adrs=NULL; + seenrootserv=1; + nseg=rhnsegcnt(name); + if(nseg>=2) { + static const unsigned char rhn_arpa[6]= {4,'a','r','p','a',0}; + unsigned int rem; + /* Check if the queried name ends in "arpa" */ + domain_match(rhn_arpa, name, &rem,NULL); + if(rem==0) mseg=3; + } + if(nseg<=mseg) { + if(nseg>0) mseg=nseg-1; else mseg=0; + } + for(;mseg>=1; --mseg) { + topdomain=skipsegs(name,nseg-mseg); + adrs=lookup_ns(topdomain); + l=DA_NEL(adrs); + if(l>0) break; + if(adrs) da_free(adrs); + } + if(l>0) { + /* The name servers for this top level domain have been found in the cache. + Instead of asking the root server, we will use this cached information. + */ + int k=0, kstart=0; + if(sp->rand_servers) k=kstart=random()%l; + if(serv_has_rejectlist(sp) && sp->rejectrecursively && !rjl) { + rjl=add_rejectlist(rejectlist,sp); + if(!rjl) {one_up=0; da_free(adrs); goto done;} + rejectlist=rjl; + } + do { + one_up=add_qserv(&serv, &DA_INDEX(adrs,k), 53, sp->timeout, + mk_flag_val(sp)&~CFF_NOINHERIT, sp->nocache, + sp->lean_query, sp->edns_query, 2, 0, + !global.paranoid, topdomain, rjl); + if(!one_up) { + da_free(adrs); + goto done; + } + if(++k==l) k=0; + } while(k!=kstart); + da_free(adrs); + DEBUG_PDNSDA_MSG("Not querying root-server %s, using cached information instead.\n", + PDNSDA2STR(PDNSD_A2_TO_A(&at->a))); + seenrootserv=2; + break; + } + } + else if(seenrootserv==2) + break; + } + if(serv_has_rejectlist(sp) && !rjl) { + rjl=add_rejectlist(rejectlist,sp); + if(!rjl) {one_up=0; goto done;} + rejectlist=rjl; + } + one_up=add_qserv(&serv, &at->a, sp->port, sp->timeout, + mk_flag_val(sp), sp->nocache, sp->lean_query, sp->edns_query, + sp->rootserver?3:(!sp->is_proxy), + needs_testing(sp), 1, NULL, rjl); + if(!one_up) + goto done; + } + if(++j==m) j=0; + } while(j!=jstart); + } + } + } + done: + unlock_server_data(); + if (one_up) { + dns_cent_t *cached; + int nocache; + rc=p_recursive_query(serv, name, thint, &cached, &nocache, hops, NULL, qhlist, c_soa); + if (rc==RC_OK) { + if (!nocache) { + dns_cent_t *tc; + add_cache(cached); + if ((tc=lookup_cache(name,NULL))) { + /* The cache may hold more information than the recent query yielded. + * try to get the merged record. If that fails, revert to the new one. */ + free_cent(cached DBG1); + pdnsd_free(cached); + cached=tc; + /* rc=RC_CACHED; */ + } else + DEBUG_MSG("p_dns_resolve: merging answer with cache failed, using local cent copy.\n"); + } else + DEBUG_MSG("p_dns_resolve: nocache.\n"); + + *cachedp=cached; + } + else if(rc==RC_CACHED || rc==RC_STALE) + *cachedp=cached; + } + else { + DEBUG_MSG("No server is marked up and allowed for this domain.\n"); + rc=RC_SERVFAIL; /* No server up */ + } + del_qserv(serv); + free_rejectlist(rejectlist); + return rc; +} + +static int set_flags_ttl(unsigned short *flags, time_t *ttl, dns_cent_t *cached, int tp) +{ + rr_set_t *rrset=getrrset(cached,tp); + if (rrset) { + time_t t; + *flags|=rrset->flags; + t=rrset->ts+CLAT_ADJ(rrset->ttl); + if (!*ttl || *ttl>t) + *ttl=t; + return 1; + } + return 0; +} + +static void set_all_flags_ttl(unsigned short *flags, time_t *ttl, dns_cent_t *cached) +{ + int i, ilim= RRARR_LEN(cached); + + for(i=0; iflags; + t=rrset->ts+CLAT_ADJ(rrset->ttl); + if (!*ttl || *ttl>t) + *ttl=t; + } + } +} + +/* + Lookup name in the cache, and if records of type thint are found, check whether a requery is needed. + Possible returns values are: + RC_OK: the name is locally defined. + RC_NAMEERR: the name is locally negatively cached. + RC_CACHED: name was found in the cache, requery not needed. + RC_STALE: name was found in the cache, but requery is needed. + RC_NOTCACHED: name was not found in the cache. +*/ +static int lookup_cache_status(const unsigned char *name, int thint, dns_cent_t **cachedp, unsigned short *flagsp, + time_t queryts, unsigned char *c_soa) +{ + dns_cent_t *cached; + int rc=RC_NOTCACHED; + int wild=0; + unsigned short flags=0; + + if ((cached=lookup_cache(name,&wild))) { + short int neg=0,timed=0,need_req=0; + time_t ttl=0; + + if (cached->flags&DF_LOCAL) { +#if DEBUG>0 + { + char dflagstr[DFLAGSTRLEN]; + DEBUG_RHN_MSG("Entry found in cache for '%s' with dflags=%s.\n", + RHN2STR(cached->qname),dflags2str(cached->flags,dflagstr)); + } +#endif + if((cached->flags&DF_NEGATIVE) || wild==w_locnerr) { + if(c_soa) { + if(cached->c_soa!=cundef) + *c_soa=cached->c_soa; + else if(have_rr_SOA(cached)) + *c_soa=rhnsegcnt(cached->qname); + else { + unsigned char *owner=getlocalowner(cached->qname,T_SOA); + if(owner) + *c_soa=rhnsegcnt(owner); + } + } + free_cent(cached DBG1); + pdnsd_free(cached); + rc= RC_NAMEERR; + goto return_rc; + } + else { + rc= RC_OK; + goto return_rc_cent; + } + } + DEBUG_RHN_MSG("Record found in cache for %s\n",RHN2STR(cached->qname)); + if (cached->flags&DF_NEGATIVE) { + if ((ttl=cached->neg.ts+CLAT_ADJ(cached->neg.ttl))>=queryts) + neg=1; + else + timed=1; + } else { + if (thint==QT_ALL) { + set_all_flags_ttl(&flags, &ttl, cached); + } + else if (!set_flags_ttl(&flags, &ttl, cached, T_CNAME) || (getrrset_CNAME(cached)->flags&CF_NEGATIVE)) { + flags=0; ttl=0; + if (thint>=T_MIN && thint<=T_MAX) { + if (set_flags_ttl(&flags, &ttl, cached, thint)) + neg=getrrset(cached,thint)->flags&CF_NEGATIVE && ttl>=queryts; + } + else if (thint==QT_MAILB) { + set_flags_ttl(&flags, &ttl, cached, T_MB); + set_flags_ttl(&flags, &ttl, cached, T_MG); + set_flags_ttl(&flags, &ttl, cached, T_MR); + } + else if (thint==QT_MAILA) { + set_flags_ttl(&flags, &ttl, cached, T_MD); + set_flags_ttl(&flags, &ttl, cached, T_MF); + } + } + if(!(flags&CF_LOCAL)) { + if (thint==QT_ALL) { + if(!(cached->flags&DF_AUTH)) + need_req=1; + } + else if (thint>=QT_MIN && thint<=QT_MAX) { + if(!(flags&CF_AUTH && !(flags&CF_ADDITIONAL))) + need_req=1; + } + if (ttl0 + { + char dflagstr[DFLAGSTRLEN],cflagstr[CFLAGSTRLEN]; + DEBUG_MSG("Requery decision: dflags=%s, cflags=%s, req=%i, neg=%i, timed=%i, %s=%li\n", + dflags2str(cached->flags,dflagstr),cflags2str(flags,cflagstr),need_req,neg,timed, + ttl?"ttl":"timestamp",(long)(ttl?(ttl-queryts):ttl)); + } +#endif + rc = (!neg && (need_req || timed))? RC_STALE: RC_CACHED; + return_rc_cent: + *cachedp=cached; + } + +return_rc: + if(flagsp) *flagsp=flags; + return rc; +} + + +/* + * Resolve records for name into dns_cent_t, type thint. + * q is the set of servers to query from. Set q to NULL if you want to ask the servers registered with pdnsd. + * qslist should refer to a list of server arrays already used higher up the calling chain (may be NULL). + * p_dns_cached_resolve() returns one of the following values: + * RC_OK means that the name was successfully resolved by querying other servers. + * RC_CACHED or RC_STALE means that the name was found in the cache. + * RC_NAMEERR or RC_SERVFAIL indicates a resolve error. + */ +static int p_dns_cached_resolve(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa) +{ + dns_cent_t *cached=NULL; + int rc; + unsigned short flags=0; + + DEBUG_RHN_MSG("Starting cached resolve for: %s, query %s\n",RHN2STR(name),get_tname(thint)); + rc= lookup_cache_status(name, thint, &cached, &flags,queryts,c_soa); + if(rc==RC_OK) { + /* Locally defined record. */ + *cachedp=cached; + return RC_CACHED; + } + else if(rc==RC_NAMEERR) /* Locally negated name. */ + return RC_NAMEERR; + + /* update server records set onquery */ + if(global.onquery) test_onquery(); + if (global.lndown_kluge && !(flags&CF_LOCAL)) { + int i,n,linkdown=1; + lock_server_data(); + n=DA_NEL(servers); + for(i=0;irootserver<=1) { + int j,m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).is_up) { + linkdown=0; + goto done; + } + } + } + } + done: + unlock_server_data(); + if (linkdown) { + DEBUG_MSG("Link is down.\n"); + rc=RC_SERVFAIL; + goto cleanup_return; + } + } + if (rc!=RC_CACHED) { + dns_cent_t *ent; + DEBUG_MSG("Trying name servers.\n"); + if (q) + rc=p_recursive_query(q,name,thint, &ent,NULL,hops,qslist,qhlist,c_soa); + else + rc=p_dns_resolve(name,thint, &ent,hops,qhlist,c_soa); + + if(rc==RC_OK || rc==RC_CACHED || rc==RC_STALE) { + if (cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + cached=ent; + } + else if (rc==RC_SERVFAIL && cached && (flags&CF_NOPURGE)) { + /* We could not get a new record, but we have a timed-out cached one + with the nopurge flag set. This means that we shall use it even + if timed out when no new one is available*/ + DEBUG_MSG("Falling back to cached record.\n"); + rc=RC_STALE; + } + else + goto cleanup_return; + } else { + DEBUG_MSG("Using cached record.\n"); + } + *cachedp=cached; + return rc; + + cleanup_return: + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + return rc; +} + + +/* r_dns_cached_resolve() is like p_dns_cached_resolve(), except that r_dns_cached_resolve() + will not return negatively cached entries, but returns RC_NAMEERR instead. + It also does not return RC_CACHED or RC_STALE, but RC_OK instead. +*/ +int r_dns_cached_resolve(unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa) +{ + dns_cent_t *cached; + int rc=p_dns_cached_resolve(NULL,name,thint,&cached,hops,NULL,qhlist,queryts,c_soa); + if(rc==RC_OK || rc==RC_CACHED || rc==RC_STALE) { + if(cached->flags&DF_NEGATIVE) { + if(c_soa) + *c_soa=cached->c_soa; + free_cent(cached DBG1); + pdnsd_free(cached); + return RC_NAMEERR; + } + else { + *cachedp=cached; + return RC_OK; + } + } + return rc; +} + + +static int simple_dns_cached_resolve(atup_array atup_a, int port, char edns_query, time_t timeout, + const unsigned char *name, int thint, dns_cent_t **cachedp) +{ + dns_cent_t *cached=NULL; + int rc; + + DEBUG_RHN_MSG("Starting simple cached resolve for: %s, query %s\n",RHN2STR(name),get_tname(thint)); + rc= lookup_cache_status(name, thint, &cached, NULL, time(NULL), NULL); + if(rc==RC_OK) { + /* Locally defined record. */ + *cachedp=cached; + return RC_OK; + } + else if(rc==RC_NAMEERR) /* Locally negated name. */ + return RC_NAMEERR; + + if (rc!=RC_CACHED) { + query_stat_array qserv; + int j,m; + if (cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + cached=NULL; + } + DEBUG_MSG("Trying name servers.\n"); + qserv=NULL; + m=DA_NEL(atup_a); + for(j=0; jflags&DF_NEGATIVE) { + free_cent(cached DBG1); + pdnsd_free(cached); + return RC_NAMEERR; + } + + *cachedp=cached; + return RC_OK; +} + + +/* Check whether a server is responsive by sending it an (empty) query. + rep is the number of times this is tried in case of no reply. + */ +int query_uptest(pdnsd_a *addr, int port, const unsigned char *name, time_t timeout, int rep) +{ + query_stat_t qs; + int iter=0,rv; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&qs.a.sin4,0,sizeof(qs.a.sin4)); + qs.a.sin4.sin_family=AF_INET; + qs.a.sin4.sin_port=htons(port); + qs.a.sin4.sin_addr=addr->ipv4; + SET_SOCKA_LEN4(qs.a.sin4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&qs.a.sin6,0,sizeof(qs.a.sin6)); + qs.a.sin6.sin6_family=AF_INET6; + qs.a.sin6.sin6_port=htons(port); + qs.a.sin6.sin6_flowinfo=IPV6_FLOWINFO; + qs.a.sin6.sin6_addr=addr->ipv6; + SET_SOCKA_LEN6(qs.a.sin6); + + qs.a4fallback.s_addr=INADDR_ANY; + } +#endif + qs.timeout=timeout; + qs.flags=0; + qs.nocache=0; + qs.auth_serv=0; + qs.lean_query=1; + qs.edns_query=0; + qs.needs_testing=0; + qs.trusted=1; + qs.aa=0; + qs.tc=0; + qs.ra=0; + qs.failed=0; + qs.nsdomain=NULL; + qs.rejectlist=NULL; + + try_again: + qs.state=QS_INITIAL; + qs.qm=global.query_method; + qs.s_errno=0; + rv=p_exec_query(NULL, name, T_A, &qs, NULL, NULL); + if(rv==-1) { + time_t ts, tpassed; + for(ts=time(NULL), tpassed=0;; tpassed=time(NULL)-ts) { + int event; +#ifdef NO_POLL + fd_set reads; + fd_set writes; + struct timeval tv; + FD_ZERO(&reads); + FD_ZERO(&writes); + PDNSD_ASSERT(qs.socktpassed?timeout-tpassed:0; + tv.tv_usec=0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that doing + this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + p_cancel_query(&qs); + return 0; + } + event=select(qs.sock+1,&reads,&writes,NULL,&tv); +#else + struct pollfd pfd; + pfd.fd=qs.sock; + switch (qs.state) { + QS_READ_CASES: + pfd.events=POLLIN; + break; + QS_WRITE_CASES: + pfd.events=POLLOUT; + break; + default: + pfd.events=0; + } + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that doing + this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + p_cancel_query(&qs); + return 0; + } + event=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + if (event<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else + log_warn("poll/select failed: %s",strerror(errno)); + p_cancel_query(&qs); + return 0; + } + if(event==0) { + /* timed out */ + p_cancel_query(&qs); + if(++iter. +*/ + + +#ifndef DNS_QUERY_H +#define DNS_QUERY_H + +#include "cache.h" + +/* Default UDP buffer size (when EDNS is not used). */ +#define UDP_BUFSIZE 512 + + +typedef struct qhintnode_s qhintnode_t; + +/* --- parallel query */ +int r_dns_cached_resolve(unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa); +#define dns_cached_resolve(name,thint,cachedp,hops,queryts,c_soa) \ + r_dns_cached_resolve(name,thint,cachedp,hops,NULL,queryts,c_soa) + +addr2_array dns_rootserver_resolv(atup_array atup_a, int port, char edns_query, time_t timeout); +int query_uptest(pdnsd_a *addr, int port, const unsigned char *name, time_t timeout, int rep); + +/* --- from dns_answer.c */ +int add_opt_pseudo_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned short udpsize, unsigned short rcode, + unsigned short ednsver, unsigned short Zflags); +size_t remove_opt_pseudo_rr(dns_msg_t *ans, size_t sz); + +#endif diff --git a/app/src/main/jni/pdnsd/src/error.c b/app/src/main/jni/pdnsd/src/error.c new file mode 100644 index 0000000..4584866 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/error.c @@ -0,0 +1,142 @@ +/* error.c - Error handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "helpers.h" +#include "conff.h" + + +pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER; +volatile short int use_log_lock=0; + +/* + * Initialize a mutex for io-locking in order not to produce gibberish on + * multiple simultaneous errors. + */ +/* This is now defined as an inline function in error.h */ +#if 0 +void init_log_lock(void) +{ + use_log_lock=1; +} +#endif + +/* We crashed? Ooops... */ +void crash_msg(char *msg) +{ + log_error("%s", msg); + log_error("pdnsd probably crashed due to a bug. Please consider sending a bug"); + log_error("report to p.a.rombouts@home.nl or tmoestl@gmx.net"); +} + +/* Log a warning, error or info message. + * If we are a daemon, use the syslog. s is a format string like in printf, + * the optional following arguments are the arguments like in printf */ +void log_message(int prior, const char *s, ...) +{ + int gotlock=0; + va_list va; + FILE *f; + + if (use_log_lock) { + gotlock=softlock_mutex(&loglock); + /* If we failed to get the lock and the type of the + message is "info" or less important, then don't bother. */ + if(!gotlock && prior>=LOG_INFO) + return; + } + if (global.daemon) { + openlog("pdnsd",LOG_PID,LOG_DAEMON); + va_start(va,s); + vsyslog(prior,s,va); + va_end(va); + closelog(); + } + else { + f=stderr; +#if DEBUG > 0 + goto printtofile; + } + if(debug_p) { + f=dbg_file; + printtofile: +#endif + { + char ts[sizeof "* 12/31 23:59:59| "]; + time_t tt = time(NULL); + struct tm tm; + + if(!localtime_r(&tt, &tm) || strftime(ts, sizeof(ts), "* %m/%d %T| ", &tm) <=0) + ts[0]=0; + fprintf(f,"%spdnsd: %s: ", ts, + prior<=LOG_CRIT?"critical": + prior==LOG_ERR?"error": + prior==LOG_WARNING?"warning": + "info"); + } + va_start(va,s); + vfprintf(f,s,va); + va_end(va); + { + const char *p=strchr(s,0); + if(!p || p==s || *(p-1)!='\n') + fputc('\n',f); + } + } + if (gotlock) + pthread_mutex_unlock(&loglock); +} + + +#if DEBUG > 0 +/* XXX: The timestamp generation makes this a little heavy-weight */ +void debug_msg(int c, const char *fmt, ...) +{ + va_list va; + + if (!c) { + char ts[sizeof "12/31 23:59:59"]; + time_t tt = time(NULL); + struct tm tm; + unsigned *id; + + if(localtime_r(&tt, &tm) && strftime(ts, sizeof(ts), "%m/%d %T", &tm) > 0) { + if((id = (unsigned *)pthread_getspecific(thrid_key))) + fprintf(dbg_file,"%u %s| ", *id, ts); + else + fprintf(dbg_file,"- %s| ", ts); + } + } + va_start(va,fmt); + vfprintf(dbg_file,fmt,va); + va_end(va); + fflush(dbg_file); +} +#endif /* DEBUG */ diff --git a/app/src/main/jni/pdnsd/src/error.h b/app/src/main/jni/pdnsd/src/error.h new file mode 100644 index 0000000..1678744 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/error.h @@ -0,0 +1,115 @@ +/* error.h - Error handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef ERROR_H +#define ERROR_H + +#include +#include +#include +#include +#include +#include + +#include "thread.h" +#include "helpers.h" +#include "pdnsd_assert.h" + +/* --- from error.c */ +extern volatile short int use_log_lock; +/* --- */ + +void crash_msg(char *msg); + +inline static void init_log_lock(void) __attribute__((always_inline)); +inline static void init_log_lock(void) +{ + use_log_lock=1; +} + +void log_message(int prior,const char *s, ...) printfunc(2, 3); +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +#define log_error(args...) log_message(LOG_ERR,args) +#define log_warn(args...) log_message(LOG_WARNING,args) +#define log_info(level,args...) {if((level)<=global.verbosity) log_message(LOG_INFO,args);} +#else +/* ANSI C99 style. */ +#define log_error(...) log_message(LOG_ERR,__VA_ARGS__) +#define log_warn(...) log_message(LOG_WARNING,__VA_ARGS__) +#define log_info(level,...) {if((level)<=global.verbosity) log_message(LOG_INFO,__VA_ARGS__);} +#endif + +/* Following are some ugly macros for debug messages that + * should inhibit any code generation when DEBUG is not defined. + * Of course, those messages could be done in a function, but I + * want to save the overhead when DEBUG is not defined. + * debug_p needs to be defined (by including conff.h), or you + * will get strange errors. + * A macro call expands to a complete statement, so a semicolon after + * the macro call is redundant. + * The arguments are normal printfs, so you know how to use the args + */ +#if DEBUG>0 +void debug_msg(int c, const char *fmt, ...) printfunc(2, 3); +/* from main.c */ +extern FILE *dbg_file; +#endif + +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +# if DEBUG > 0 +# define DEBUG_MSG(args...) {if (debug_p) debug_msg(0,args);} +# define DEBUG_MSGC(args...) {if (debug_p) debug_msg(1,args);} +# define DEBUG_PDNSDA_MSG(args...) {char _debugsockabuf[ADDRSTR_MAXLEN]; DEBUG_MSG(args);} +# define PDNSDA2STR(a) pdnsd_a2str(a,_debugsockabuf,sizeof(_debugsockabuf)) +# define DEBUG_RHN_MSG(args...) {unsigned char _debugstrbuf[DNSNAMEBUFSIZE]; DEBUG_MSG(args);} +# define RHN2STR(a) rhn2str(a,_debugstrbuf,sizeof(_debugstrbuf)) +# else +# define DEBUG_MSG(args...) +# define DEBUG_MSGC(args...) +# define DEBUG_PDNSDA_MSG(args...) +# define DEBUG_RHN_MSG(args...) +# endif /* DEBUG > 0 */ +#else +/* ANSI C99 style. */ +# if DEBUG > 0 +/* + * XXX: The ANSI and GCC variadic macros should be merged as far as possible, but that + * might make things even more messy... + */ +# define DEBUG_MSG(...) {if (debug_p) debug_msg(0,__VA_ARGS__);} +# define DEBUG_MSGC(...) {if (debug_p) debug_msg(1,__VA_ARGS__);} +# define DEBUG_PDNSDA_MSG(...) {char _debugsockabuf[ADDRSTR_MAXLEN]; DEBUG_MSG(__VA_ARGS__);} +# define PDNSDA2STR(a) pdnsd_a2str(a,_debugsockabuf,ADDRSTR_MAXLEN) +# define DEBUG_RHN_MSG(...) {unsigned char _debugstrbuf[DNSNAMEBUFSIZE]; DEBUG_MSG(__VA_ARGS__);} +# define RHN2STR(a) rhn2str(a,_debugstrbuf,sizeof(_debugstrbuf)) +# else +# define DEBUG_MSG(...) +# define DEBUG_MSGC(...) +# define DEBUG_PDNSDA_MSG(...) +# define DEBUG_RHN_MSG(...) +# endif /* DEBUG > 0 */ +#endif + +#endif diff --git a/app/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h b/app/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h new file mode 100644 index 0000000..e4577ce --- /dev/null +++ b/app/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/netinet/ip_icmp.h,v 1.20 2003/03/21 15:28:10 mdodd Exp $ + */ + +#ifndef _NETINET_IP_ICMP_H_ +#define _NETINET_IP_ICMP_H_ + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +/* + * Internal of an ICMP Router Advertisement + */ +struct icmp_ra_addr { + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + +/* + * Structure of an icmp header. + */ +struct icmp { + u_char icmp_type; /* type of message, see below */ + u_char icmp_code; /* type sub code */ + u_short icmp_cksum; /* ones complement cksum of struct */ + union { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + n_short icd_id; + n_short icd_seq; + } ih_idseq; + int ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + n_short ipm_void; + n_short ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv { + u_char irt_num_addrs; + u_char irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct id_ts { /* ICMP Timestamp */ + n_time its_otime; /* Originate */ + n_time its_rtime; /* Receive */ + n_time its_ttime; /* Transmit */ + } id_ts; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + char id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enough to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohib */ +#define ICMP_UNREACH_HOST_PRECEDENCE 14 /* host prec vio. */ +#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* prec cutoff */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_ERRATPTR 0 /* error at param ptr */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ +#define ICMP_PARAMPROB_LENGTH 2 /* bad length */ +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ + +#define ICMP_MAXTYPE 18 + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + +#ifdef _KERNEL +void icmp_error(struct mbuf *, int, int, n_long, struct ifnet *); +void icmp_input(struct mbuf *, int); +#endif + +#endif diff --git a/app/src/main/jni/pdnsd/src/hash.c b/app/src/main/jni/pdnsd/src/hash.c new file mode 100644 index 0000000..12e2074 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/hash.c @@ -0,0 +1,322 @@ +/* hash.c - Manage hashes for cached dns records + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include "hash.h" +#include "cache.h" +#include "error.h" +#include "helpers.h" +#include "consts.h" + + +/* This is not a perfect hash, but I hope it holds. It is designed for 1024 hash + * buckets, and hashes strings with case-insensitivity. + * It is position-aware in a limited way. + * It is exactly seen a two-way hash: because I do not want to exaggerate + * the hash buckets (i do have 1024), but I hash strings and string-comparisons + * are expensive, I save another 32 bit hash in each hash element that is checked + * before the string. The 32 bit hash is also used to order the entries in a hash chain. + * I hope not to have all too much collision concentration. + * + * The ip hash was removed. I don't think it concentrated the collisions too much. + * If it does, the hash algorithm needs to be changed, rather than using another + * hash. + * Some measurements seem to indicate that the hash algorithm is doing reasonable well. + */ + +dns_hash_ent_t *hash_buckets[HASH_NUM_BUCKETS]; + + +/* + * Hash a dns name (length-byte string format) to HASH_SZ bit. + * *rhash is set to a long int hash. + */ +static unsigned dns_hash(const unsigned char *str, unsigned long *rhash) +{ + unsigned s,i,lb,c; + unsigned long r; + s=0; r=0; + i=0; + while((lb=str[i])) { + s+=lb<<(i%(HASH_SZ-5)); + r+=((unsigned long)lb)<<(i%(8*sizeof(unsigned long)-7)); + ++i; + do { + c=toupper(str[i]); + s+=c<<(i%(HASH_SZ-5)); + r+=((unsigned long)c)<<(i%(8*sizeof(unsigned long)-7)); + ++i; + } while(--lb); + } + s=(s&HASH_BITMASK)+((s&(~HASH_BITMASK))>>HASH_SZ); + s=(s&HASH_BITMASK)+((s&(~HASH_BITMASK))>>HASH_SZ); + s &= HASH_BITMASK; +#ifdef DEBUG_HASH + { + unsigned char buf[DNSNAMEBUFSIZE]; + printf("Diagnostic: hashes for %s: %03x,%04lx\n",rhn2str(str,buf,sizeof(buf)),s,r); + } +#endif + if(rhash) *rhash=r; + return s; +} + +/* + * Initialize hash to hold a dns hash table + */ +/* This is now defined as an inline function in hash.h */ +#if 0 +void mk_dns_hash() +{ + int i; + for(i=0;irhash<=rh) { + if (he->rhash==rh && rhnicmp(key,he->data->qname)) { + retval = he->data; + break; + } + hep = &he->next; + } + if(loc) { + loc->pos = hep; + loc->rhash = rh; + } + return retval; +} + +/* + Add a cache entry to the hash table. + + loc must contain the location where the the new entry should be inserted + (this location can be obtained with dns_lookup). + + add_dns_hash returns 1 on success, or 0 if out of memory. +*/ +int add_dns_hash(dns_cent_t *data, dns_hash_loc_t *loc) +{ + dns_hash_ent_t *he = malloc(sizeof(dns_hash_ent_t)); + + if(!he) + return 0; + + he->next = *(loc->pos); + he->rhash = loc->rhash; + he->data = data; + *(loc->pos) = he; + + return 1; +} + +/* + Delete the hash entry indentified by the location returned by dns_lookup(). +*/ +dns_cent_t *del_dns_hash_ent(dns_hash_loc_t *loc) +{ + dns_hash_ent_t *he = *(loc->pos); + dns_cent_t *data; + + *(loc->pos) = he->next; + data = he->data; + free(he); + return data; +} + +/* + * Delete the first entry indexed by key from the hash. Returns the data field or NULL. + * Since two cents are not allowed to be for the same host name, there will be only one. + */ +dns_cent_t *del_dns_hash(const unsigned char *key) +{ + unsigned idx; + unsigned long rh; + dns_hash_ent_t **hep,*he; + dns_cent_t *data; + + idx = dns_hash(key,&rh); + hep = &hash_buckets[idx]; + while ((he= *hep) && he->rhash<=rh) { + if (he->rhash==rh && rhnicmp(key,he->data->qname)) { + *hep = he->next; + data = he->data; + free(he); + return data; + } + hep = &he->next; + } + return NULL; /* not found */ +} + + +/* + * Delete all entries in a hash bucket. + */ +void free_dns_hash_bucket(int i) +{ + dns_hash_ent_t *he,*hen; + + he=hash_buckets[i]; + hash_buckets[i]=NULL; + while (he) { + hen=he->next; + del_cent(he->data); + free(he); + he=hen; + } +} + +/* + * Delete all entries in a hash bucket whose names match those in + * an include/exclude list. + */ +void free_dns_hash_selected(int i, slist_array sla) +{ + dns_hash_ent_t **hep,*he,*hen; + int j,m=DA_NEL(sla); + + hep= &hash_buckets[i]; + he= *hep; + + while (he) { + unsigned char *name=he->data->qname; + for(j=0;jdomain,&nrem,&lrem); + if(!lrem && (!sl->exact || !nrem)) { + if(sl->rule==C_INCLUDED) + goto delete_entry; + else + break; + } + } + /* default policy is not to delete */ + hep= &he->next; + he= *hep; + continue; + + delete_entry: + *hep=hen=he->next;; + del_cent(he->data); + free(he); + he=hen; + } +} + +/* + * Delete the whole hash table, freeing all memory + */ +void free_dns_hash() +{ + int i; + dns_hash_ent_t *he,*hen; + for (i=0;inext; + del_cent(he->data); + free(he); + he=hen; + } + } +} + +/* + * The following functions are for iterating over the hash. + * fetch_first returns the data field of the first element (or NULL if there is none), and fills pos + * for subsequent calls of fetch_next. + * fetch_next returns the data field of the element after the element that was returned by the last + * call with the same position argument (or NULL if there is none) + * + * Note that these are designed so that you may actually delete the elements you retrieved from the hash. + */ +dns_cent_t *fetch_first(dns_hash_pos_t *pos) +{ + int i; + for (i=0;ibucket=i; + pos->ent=he->next; + return he->data; + } + } + return NULL; +} + +dns_cent_t *fetch_next(dns_hash_pos_t *pos) +{ + dns_hash_ent_t *he=pos->ent; + int i; + if (he) { + pos->ent=he->next; + return he->data; + } + + for (i=pos->bucket+1;ibucket=i; + pos->ent=he->next; + return he->data; + } + } + return NULL; +} + +#ifdef DEBUG_HASH +void dumphash() +{ + if(debug_p) { + int i, j; + dns_hash_ent_t *he; + + for (i=0; inext, j++) ; + DEBUG_MSG("bucket %d: %d entries\n", i, j); + } + } +} +#endif diff --git a/app/src/main/jni/pdnsd/src/hash.h b/app/src/main/jni/pdnsd/src/hash.h new file mode 100644 index 0000000..db25a34 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/hash.h @@ -0,0 +1,83 @@ +/* hash.h - Manage hashes for cached dns records + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _HASH_H_ +#define _HASH_H_ +#include +#include "cache.h" + +typedef struct dns_hash_ent_s { + struct dns_hash_ent_s *next; + unsigned long rhash; /* this is a better hash */ + dns_cent_t *data; +} dns_hash_ent_t; + +/* Redefine this if you want another hash size. Should work ;-). + * The number of hash buckets is computed as power of two; + * so, e.g. HASH_SZ set to 10 yields 1024 hash rows (2^10 or 1<<10). + * Only powers of two are possible conveniently. + * HASH_SZ may not be bigger than 32 (if you set it even close to that value, + * you are nuts.) */ +/* #define HASH_SZ 10 */ /* Now defined in config.h */ +#define HASH_NUM_BUCKETS (1<. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "thread.h" +#include "error.h" +#include "helpers.h" +#include "cache.h" +#include "conff.h" + + +/* + * This is to exit pdnsd from any thread. + */ +void pdnsd_exit() +{ + pthread_kill(main_thrid,SIGTERM); + pthread_exit(NULL); +} + +/* + * Try to grab a mutex. If we can't, fail. This will loop until we get the + * mutex or fail. This is only used in debugging code or at exit, otherwise + * we might run into lock contention problems. + */ +int softlock_mutex(pthread_mutex_t *mutex) +{ + unsigned int tr=0; + while(pthread_mutex_trylock(mutex)) { + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(10000); + } + return 1; +} + +/* + * setuid() and setgid() for a specified user. + */ +int run_as(const char *user) +{ + if (user[0]) { +#ifdef HAVE_GETPWNAM_R + struct passwd pwdbuf, *pwd; + size_t buflen; + int err; + + for(buflen=128;; buflen*=2) { + char buf[buflen]; /* variable length array */ + + /* Note that we use getpwnam_r() instead of getpwnam(), + which returns its result in a statically allocated buffer and + cannot be considered thread safe. + Doesn't use NSS! */ + err=getpwnam_r(user, &pwdbuf, buf, buflen, &pwd); + if(err==0 && pwd) { + /* setgid first, because we may not be allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid)!=0) { + log_error("Could not change group id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + + /* initgroups uses NSS, so we can disable it, + i.e. we might need DNS for LDAP lookups, which times out */ + if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) { + log_error("Could not initialize the group access list of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + if (setuid(pwd->pw_uid)!=0) { + log_error("Could not change user id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + break; + } + else if(err!=ERANGE) { + if(err) + log_error("run_as user '%s' could not be found: %s",user,strerror(err)); + else + log_error("run_as user '%s' could not be found.",user); + return 0; + } + else if(buflen>=16*1024) { + /* If getpwnam_r() seems defective, call it quits rather than + keep on allocating ever larger buffers until we crash. */ + log_error("getpwnam_r() requires more than %u bytes of buffer space.",(unsigned)buflen); + return 0; + } + /* Else try again with larger buffer. */ + } +#else + /* No getpwnam_r() :-( We'll use getpwnam() and hope for the best. */ + struct passwd *pwd; + + if (!(pwd=getpwnam(user))) { + log_error("run_as user %s could not be found.",user); + return 0; + } + /* setgid first, because we may not allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid)!=0) { + log_error("Could not change group id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + /* initgroups uses NSS, so we can disable it, + i.e. we might need DNS for LDAP lookups, which times out */ + if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) { + log_error("Could not initialize the group access list of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + if (setuid(pwd->pw_uid)!=0) { + log_error("Could not change user id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } +#endif + } + + return 1; +} + +/* + * returns whether c is allowed in IN domain names + */ +#if 0 +int isdchar (unsigned char c) +{ + if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='-' +#ifdef UNDERSCORE + || c=='_' +#endif + ) + return 1; + return 0; +} +#endif + +/* + * Convert a string given in dotted notation to the transport format (length byte prepended + * domain name parts, ended by a null length sequence) + * The memory areas referenced by str and rhn may not overlap. + * The buffer rhn points to is assumed to be at least DNSNAMEBUFSIZE bytes in size. + * + * Returns 1 if successful, otherwise 0. + */ +int str2rhn(const unsigned char *str, unsigned char *rhn) +{ + unsigned int i,j; + + if(*str=='.' && !*(str+1)) { + /* Special case: root domain */ + rhn[0]=0; + return 1; + } + + for(i=0;;) { + unsigned int jlim= i+63; + if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2; /* DNSNAMEBUFSIZE-2 because the termination 0 has to follow */ + for(j=i; str[j] && str[j]!='.'; ++j) { + if(j>=jlim) return 0; + rhn[j+1]=str[j]; + } + if(!str[j]) + break; + if(j<=i) + return 0; + rhn[i]=(unsigned char)(j-i); + i = j+1; + } + + rhn[i]=0; + if (j>i || i==0) + return 0; + return 1; +} + +/* + parsestr2rhn is essentially the same as str2rhn, except that it doesn't look beyond + the first len chars in the input string. It also tolerates strings + not ending in a dot and returns a message in case of an error. + */ +const char *parsestr2rhn(const unsigned char *str, unsigned int len, unsigned char *rhn) +{ + unsigned int i,j; + + if(len>0 && *str=='.' && (len==1 || !*(str+1))) { + /* Special case: root domain */ + rhn[0]=0; + return NULL; + } + + i=0; + do { + unsigned int jlim= i+63; + if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2; + for(j=i; j=jlim) + return "Domain name element too long"; + rhn[j+1]=str[j]; + } + + if(j<=i) { + if(j=2) + str[j++]='.'; + } + else { + do { + for (;lb;--lb) { + unsigned char c; + if(j+2>=size) + goto overflow; + c=rhn[i++]; + if(isgraph(c)) { + if(c=='.' || c=='\\' || c=='"') { + str[j++]='\\'; + if(j+2>=size) + goto overflow; + } + str[j++]=c; + } + else { + unsigned int rem=size-1-j; + int n=snprintf(charp &str[j],rem,"\\%03o",c); + if(n<0 || n>=rem) { + str[j++]='.'; + goto overflow; + } + j+=n; + } + } + str[j++]='.'; + lb=rhn[i++]; + } while(lb); + } + str[j]=0; + return str; + + overflow: + j=size; + str[--j]=0; + if(j>0) { + str[--j]='.'; + if(j>0) { + str[--j]='.'; + if(j>0) + str[--j]='.'; + } + } + return str; +} + +/* Return the length of a domain name in transport format. + The definition has in fact been moved to helpers.h as an inline function. + Note added by Paul Rombouts: + Compared to the definition used by Thomas Moestl (strlen(rhn)+1), the following definition of rhnlen + may yield a different result in certain error situations (when a domain name segment contains null byte). +*/ +#if 0 +unsigned int rhnlen(const unsigned char *rhn) +{ + unsigned int i=0,lb; + + while((lb=rhn[i++])) + i+=lb; + return i; +} +#endif + +/* + * Non-validating rhn copy (use with checked or generated data only). + * Returns number of characters copied. The buffer dst points to is assumed to be DNSNAMEBUFSIZE (or + * at any rate large enough) bytes in size. + * The answer assembly code uses this; it is guaranteed to not clobber anything + * after the name. + */ +unsigned int rhncpy(unsigned char *dst, const unsigned char *src) +{ + unsigned int len = rhnlen(src); + + PDNSD_ASSERT(len<=DNSNAMEBUFSIZE,"rhncpy: src too long!"); + memcpy(dst,src,len>DNSNAMEBUFSIZE?DNSNAMEBUFSIZE:len); + return len; +} + + +/* Check whether a name is a normal wire-encoded domain name, + i.e. is not compressed, doesn't use extended labels and is not + too long. +*/ +int isnormalencdomname(const unsigned char *rhn, unsigned maxlen) +{ + unsigned int i,lb; + + if(maxlen>DNSNAMEBUFSIZE) + maxlen=DNSNAMEBUFSIZE; + for(i=0;;) { + if(i>=maxlen) return 0; + lb=rhn[i++]; + if(lb==0) break; + if(lb>0x3f) return 0; + i += lb; + } + + return 1; +} + +int str2pdnsd_a(const char *addr, pdnsd_a *a) +{ +#ifdef ENABLE_IPV4 + if (run_ipv4) { + return inet_aton(addr,&a->ipv4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + /* Try to map an IPv4 address to IPv6 */ + struct in_addr a4; + if(inet_aton(addr,&a4)) { + a->ipv6=global.ipv4_6_prefix; + ((uint32_t *)(&a->ipv6))[3]=a4.s_addr; + return 1; + } + return inet_pton(AF_INET6,addr,&a->ipv6)>0; + } +#endif + /* return 0; */ +} + +/* definition moved to helpers.h */ +#if 0 +int is_inaddr_any(pdnsd_a *a) +{ + return SEL_IPVER( a->ipv4.s_addr==INADDR_ANY, + IN6_IS_ADDR_UNSPECIFIED(&a->ipv6) ); +} +#endif + +/* + * This is used for user output only, so it does not matter when an error occurs. + */ +const char *pdnsd_a2str(pdnsd_a *a, char *buf, int maxlen) +{ + const char *res= SEL_IPVER( inet_ntop(AF_INET,&a->ipv4,buf,maxlen), + inet_ntop(AF_INET6,&a->ipv6,buf,maxlen) ); + if (!res) { + log_error("inet_ntop: %s", strerror(errno)); + return "?.?.?.?"; + } + + return res; +} + + +/* Appropriately set our random device */ +#ifdef R_DEFAULT +# if (TARGET == TARGET_BSD) && !defined(__NetBSD__) +# define R_ARC4RANDOM 1 +# else +# define R_RANDOM 1 +# endif +#endif + +#ifdef RANDOM_DEVICE +FILE *rand_file; +#endif + +#ifdef R_RANDOM +void init_crandom() +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv,&tz); + srandom(tv.tv_sec^tv.tv_usec); /* not as guessable as time() */ +} +#endif + +/* initialize the PRNG */ +int init_rng() +{ +#ifdef RANDOM_DEVICE + if (!(rand_file=fopen(RANDOM_DEVICE,"r"))) { + log_error("Could not open %s.",RANDOM_DEVICE); + return 0; + } +#endif +#ifdef R_RANDOM + init_crandom(); +#endif + return 1; +} + +/* The following function is now actually defined as a macro in helpers.h */ +#if 0 +void free_rng() +{ +#ifdef RANDOM_DEVICE + if (rand_file) + fclose(rand_file); +#endif +} +#endif + +/* generate a (more or less) random number 16 bits long. */ +unsigned short get_rand16() +{ +#ifdef RANDOM_DEVICE + unsigned short rv; + + if (rand_file) { + if (fread(&rv,sizeof(rv),1, rand_file)!=1) { + log_error("Error while reading from random device: %s", strerror(errno)); + pdnsd_exit(); + } + return rv&0xffff; + } else + return random()&0xffff; +#endif +#ifdef R_RANDOM + return random()&0xffff; +#endif +#ifdef R_ARC4RANDOM + return arc4random()&0xffff; +#endif +} + +/* fsprintf does formatted output to a file descriptor. + The functionality is similar to fprintf, but note that fd + is of type int instead of FILE*. + This function has been rewritten by Paul Rombouts */ +int fsprintf(int fd, const char *format, ...) +{ + int n; + va_list va; + + { + char buf[256]; + + va_start(va,format); + n=vsnprintf(buf,sizeof(buf),format,va); + va_end(va); + + if(n<(int)sizeof(buf)) { + if(n>0) n=write_all(fd,buf,n); + return n; + } + } + /* retry with a right sized buffer, needs glibc 2.1 or higher to work */ + { + unsigned bufsize=n+1; + char buf[bufsize]; + + va_start(va,format); + n=vsnprintf(buf,bufsize,format,va); + va_end(va); + + if(n>0) n=write_all(fd,buf,n); + } + return n; +} + +/* Convert data into a hexadecimal representation (for debugging purposes).. + The result is stored in the character array buf. + If buf is not large enough to hold the result, the + truncation is indicated by trailing dots. +*/ +void hexdump(const void *data, int dlen, char *buf, int buflen) +{ + const unsigned char *p=data; + int i,j=0; + for(i=0;i=rem) goto truncated; + j += n; + } + goto done; + + truncated: + if(j>=6) { + j -= 3; + if(j+4>=buflen) + j -= 3; + buf[j++]=' '; + buf[j++]='.'; + buf[j++]='.'; + buf[j++]='.'; + } + else { + int ndots=buflen-1; + if(ndots>3) ndots=3; + j=0; + while(j=size) + return -1; + c=in[i]; + if(!isprint(c)) { + int rem=size-j; + int n=snprintf(&str[j],rem,"\\%03o",c); + if(n<0 || n>=rem) { + return -1; + } + j+=n; + } + else { + if(c=='\\' || c=='"') { + str[j++]='\\'; + if(j+1>=size) + return -1; + } + str[j++]=c; + } + } + str[j]=0; + return j; +} + +/* + * This is not like strcmp, but will return 1 on match or 0 if the + * strings are different. + */ +/* definition moved to helpers.h as an inline function. */ +#if 0 +int stricomp(char *a, char *b) +{ + int i; + if (strlen(a) != strlen(b)) + return 0; + for (i=0;i= dstsz-1 && o!='\0') + return 0; + return 1; +} +#endif + +#ifndef HAVE_GETLINE +/* Note by Paul Rombouts: I know that getline is a GNU extension and is not really portable, + but the alternative standard functions have some real problems. + The following substitute does not have exactly the same semantics as the GNU getline, + but it should be good enough, as long as the stream doesn't contain any null chars. + This version is actually based on fgets_realloc() that I found in the WWWOFFLE source. +*/ + +#define BUFSIZE 256 +int getline(char **lineptr, size_t *n, FILE *stream) +{ + char *line=*lineptr; + size_t sz=*n,i; + + if(!line || sz=sz) { + /* retry with a right sized buffer, needs glibc 2.1 or higher to work */ + sz=n+1; + { + char *tmp=realloc(line,sz); + if(tmp) { + line=tmp; + n=vsnprintf(line,sz,format,vasave); + } + else + n= -1; + } + } + va_end(vasave); + + if(n>=0) + *lineptr=line; + else + free(line); + return n; +} +#endif + +#ifndef HAVE_ASPRINTF +int asprintf (char **lineptr, const char *format, ...) +{ + int n; + va_list va; + + va_start(va,format); + n=vasprintf(lineptr,format,va); + va_end(va); + + return n; +} +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size) +{ + const char *rc = NULL; + + if (src != NULL && dst != NULL && size > 0) { + switch (af) { + case AF_INET: + { + const unsigned char *p=src; + int n = snprintf(dst, size, "%u.%u.%u.%u", + p[0],p[1],p[2],p[3]); + if (n >= 0 && n < size) rc = dst; + } + break; + +#ifdef AF_INET6 + case AF_INET6: + { + const unsigned char *p=src; + unsigned int i,offs=0; + for (i=0;i<16;i+=2) { + int n=snprintf(dst+offs, size-offs,i==0?"%x":":%x", ((unsigned)p[i]<<8)|p[i+1]); + if(n<0) return NULL; + offs+=n; + if(offs>=size) return NULL; + } + rc = dst; + } + break; +#endif + } + } + + return rc; +} +#endif diff --git a/app/src/main/jni/pdnsd/src/helpers.h b/app/src/main/jni/pdnsd/src/helpers.h new file mode 100644 index 0000000..e45c56e --- /dev/null +++ b/app/src/main/jni/pdnsd/src/helpers.h @@ -0,0 +1,319 @@ +/* helpers.h - Various helper functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef HELPERS_H +#define HELPERS_H + +#include +#include +#include +#include +#include +#include "cache.h" +#include "pdnsd_assert.h" + +#define SOFTLOCK_MAXTRIES 1000 + +int run_as(const char *user); +void pdnsd_exit(void); +int softlock_mutex(pthread_mutex_t *mutex); + +#if 0 +inline static int isdchar (unsigned char c) +{ + return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='-' +#ifdef UNDERSCORE + || c=='_' +#endif + ); +} +#endif + +const unsigned char *rhn2str(const unsigned char *rhn, unsigned char *str, unsigned int size); +int str2rhn(const unsigned char *str, unsigned char *rhn); +const char *parsestr2rhn(const unsigned char *str, unsigned int len, unsigned char *rhn); + +/* Note added by Paul Rombouts: + Compared to the definition used by Thomas Moestl (strlen(rhn)+1), the following definition of rhnlen + may yield a different result in certain error situations (when a domain name segment contains a null byte). +*/ +inline static unsigned int rhnlen(const unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned int rhnlen(const unsigned char *rhn) +{ + unsigned int i=0,lb; + + while((lb=rhn[i++])) + i+=lb; + return i; +} + +/* Skip k segments in a name in length-byte string notation. */ +inline static const unsigned char *skipsegs(const unsigned char *nm, unsigned k) + __attribute__((always_inline)); +inline static const unsigned char *skipsegs(const unsigned char *nm, unsigned k) +{ + unsigned lb; + for(;k && (lb= *nm); --k) { + nm += lb+1; + } + return nm; +} + +/* Skip a name in length-byte string notation and return a pointer to the + position right after the terminating null byte. +*/ +inline static unsigned char *skiprhn(unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned char *skiprhn(unsigned char *rhn) +{ + unsigned lb; + + while((lb= *rhn++)) + rhn += lb; + return rhn; +} + +/* count the number of name segments of a name in length-byte string notation. */ +inline static unsigned int rhnsegcnt(const unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned int rhnsegcnt(const unsigned char *rhn) +{ + unsigned int res=0,lb; + + while((lb= *rhn)) { + ++res; + rhn += lb+1; + } + return res; +} + +unsigned int rhncpy(unsigned char *dst, const unsigned char *src); +int isnormalencdomname(const unsigned char *rhn, unsigned maxlen); + +inline static int is_inaddr_any(pdnsd_a *a) __attribute__((always_inline)); +inline static int is_inaddr_any(pdnsd_a *a) +{ + return SEL_IPVER( a->ipv4.s_addr==INADDR_ANY, + IN6_IS_ADDR_UNSPECIFIED(&a->ipv6) ); +} + + +inline static int same_inaddr(pdnsd_a *a, pdnsd_a *b) + __attribute__((always_inline)); +inline static int same_inaddr(pdnsd_a *a, pdnsd_a *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) ); +} + +/* Compare a pdnsd_a* with a pdnsd_a2*. */ +inline static int same_inaddr2(pdnsd_a *a, pdnsd_a2 *b) + __attribute__((always_inline)); +inline static int same_inaddr2(pdnsd_a *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) && b->ipv4.s_addr==INADDR_ANY ); +} + +inline static int equiv_inaddr2(pdnsd_a *a, pdnsd_a2 *b) + __attribute__((always_inline)); +inline static int equiv_inaddr2(pdnsd_a *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) || + (b->ipv4.s_addr!=INADDR_ANY && ADDR_EQUIV6_4(&a->ipv6,&b->ipv4)) ); +} + +int str2pdnsd_a(const char *addr, pdnsd_a *a); +const char *pdnsd_a2str(pdnsd_a *a, char *buf, int maxlen); + +int init_rng(void); +#ifdef RANDOM_DEVICE +extern FILE *rand_file; +/* Because this is usually empty, it is now defined as a macro to save overhead.*/ +#define free_rng() {if (rand_file) fclose(rand_file);} +#else +#define free_rng() +#endif + +unsigned short get_rand16(void); + +int fsprintf(int fd, const char *format, ...) printfunc(2, 3); +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +# define fsprintf_or_return(args...) {int _retval; if((_retval=fsprintf(args))<0) return _retval;} +#else +/* ANSI C99 style variadic macro. */ +# define fsprintf_or_return(...) {int _retval; if((_retval=fsprintf(__VA_ARGS__))<0) return _retval;} +#endif + +/* Added by Paul Rombouts */ +inline static ssize_t write_all(int fd,const void *data,size_t n) + __attribute__((always_inline)); +inline static ssize_t write_all(int fd,const void *data,size_t n) +{ + ssize_t written=0; + + while(written +int vasprintf (char **lineptr, const char *format, va_list va); +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +#endif + +#define strlitlen(strlit) (sizeof(strlit)-1) + +#endif /* HELPERS_H */ diff --git a/app/src/main/jni/pdnsd/src/icmp.c b/app/src/main/jni/pdnsd/src/icmp.c new file mode 100644 index 0000000..6e3e46f --- /dev/null +++ b/app/src/main/jni/pdnsd/src/icmp.c @@ -0,0 +1,544 @@ +/* icmp.c - Server response tests using ICMP echo requests + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2005, 2007, 2012 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * This should now work on both Linux and FreeBSD (and CYGWIN?). If anyone + * with experience in other Unix flavors wants to contribute platform-specific + * code, he is very welcome. + */ + +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#if (TARGET==TARGET_LINUX) +# include +# include +# include +#elif (TARGET==TARGET_BSD) +# include +# include +# include +#elif (TARGET==TARGET_CYGWIN) +# include +# include +# include +# include "freebsd_netinet_ip_icmp.h" +#else +# error Unsupported platform! +#endif +#ifdef ENABLE_IPV6 +# include +# include +#endif +#include +#include "icmp.h" +#include "error.h" +#include "helpers.h" +#include "servers.h" + + +#define ICMP_MAX_ERRS 10 +static volatile unsigned long icmp_errs=0; /* This is only here to minimize log output. + Since the consequences of a race is only + one log message more/less (out of + ICMP_MAX_ERRS), no lock is required. */ + +volatile int ping_isocket=-1; +#ifdef ENABLE_IPV6 +volatile int ping6_isocket=-1; +#endif + +/* different names, same thing... be careful, as these are macros... */ +#if (TARGET==TARGET_LINUX) +# define ip_saddr saddr +# define ip_daddr daddr +# define ip_hl ihl +# define ip_p protocol +#else +# define icmphdr icmp +# define iphdr ip +# define ip_saddr ip_src.s_addr +# define ip_daddr ip_dst.s_addr +#endif + +#if (TARGET==TARGET_LINUX) +# define icmp_type type +# define icmp_code code +# define icmp_cksum checksum +# define icmp_id un.echo.id +# define icmp_seq un.echo.sequence +#else +# define ICMP_DEST_UNREACH ICMP_UNREACH +# define ICMP_TIME_EXCEEDED ICMP_TIMXCEED +#endif + +#define ICMP_BASEHDR_LEN 8 +#define ICMP4_ECHO_LEN ICMP_BASEHDR_LEN + +#if (TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +/* + * These are the ping implementations for Linux/FreeBSD in their IPv4/ICMPv4 and IPv6/ICMPv6 versions. + * I know they share some code, but I'd rather keep them separated in some parts, as some + * things might go in different directions there. + */ + +/* Initialize the sockets for pinging */ +void init_ping_socket() +{ + if ((ping_isocket=socket(PF_INET, SOCK_RAW, IPPROTO_ICMP))==-1) { + log_warn("icmp ping: socket() failed: %s",strerror(errno)); + } +#ifdef ENABLE_IPV6 + if (!run_ipv4) { + /* Failure to initialize the IPv4 ping socket is not + necessarily a problem, as long as the IPv6 version works. */ + if ((ping6_isocket=socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6))==-1) { + log_warn("icmpv6 ping: socket() failed: %s",strerror(errno)); + } + } +#endif +} + +/* Takes a packet as send out and a received ICMP packet and looks whether the ICMP packet is + * an error reply on the sent-out one. packet is only the packet (without IP header). + * errmsg includes an IP header. + * to is the destination address of the original packet (the only thing that is actually + * compared of the IP header). The RFC says that we get at least 8 bytes of the offending packet. + * We do not compare more, as this is all we need.*/ +static int icmp4_errcmp(char *packet, int plen, struct in_addr *to, char *errmsg, int elen, int errtype) +{ + struct iphdr iph; + struct icmphdr icmph; + struct iphdr eiph; + char *data; + + /* XXX: lots of memcpy to avoid unaligned accesses on alpha */ + if (elen= ICMP_BASEHDR_LEN, "icmp4_errcmp: ICMP_BASEHDR_LEN botched"); + memcpy(&icmph,errmsg+iph.ip_hl*4,ICMP_BASEHDR_LEN); + memcpy(&eiph,errmsg+iph.ip_hl*4+ICMP_BASEHDR_LEN,sizeof(eiph)); + if (elens_addr, &eiph.ip_daddr, sizeof(to->s_addr))==0 && + memcmp(data, packet, plen<8?plen:8)==0; +} + +/* IPv4/ICMPv4 ping. Called from ping (see below) */ +static int ping4(struct in_addr addr, int timeout, int rep) +{ + int i; + int isock; +#if (TARGET==TARGET_LINUX) + struct icmp_filter f; +#endif + unsigned short id=(unsigned short)get_rand16(); /* randomize a ping id */ + + isock=ping_isocket; + +#if (TARGET==TARGET_LINUX) + /* Fancy ICMP filering -- only on Linux (as far is I know) */ + + /* In fact, there should be macros for treating icmp_filter, but I haven't found them in Linux 2.2.15. + * So, set it manually and unportable ;-) */ + /* This filter lets ECHO_REPLY (0), DEST_UNREACH(3) and TIME_EXCEEDED(11) pass. */ + /* !(0000 1000 0000 1001) = 0xff ff f7 f6 */ + f.data=0xfffff7f6; + if (setsockopt(isock,SOL_RAW,ICMP_FILTER,&f,sizeof(f))==-1) { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("icmp ping: setsockopt() failed: %s", strerror(errno)); + } + return -1; + } +#endif + + for (i=0;i> 16) + (sum & 0xffff); + sum += (sum >> 16); + icmpd.icmp_cksum=~sum; + + memset(&to,0,sizeof(to)); + to.sin_family=AF_INET; + to.sin_port=0; + to.sin_addr=addr; + SET_SOCKA_LEN4(to); + if (sendto(isock,&icmpd,ICMP4_ECHO_LEN,0,(struct sockaddr *)&to,sizeof(to))==-1) { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("icmp ping: sendto() failed: %s.",strerror(errno)); + } + return -1; + } + /* listen for reply. */ + tm=time(NULL); tpassed=0; + do { + int psres; +#ifdef NO_POLL + fd_set fds,fdse; + struct timeval tv; + FD_ZERO(&fds); + PDNSD_ASSERT(isocktpassed?timeout-tpassed:0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=select(isock+1,&fds,NULL,&fdse,&tv); +#else + struct pollfd pfd; + pfd.fd=isock; + pfd.events=POLLIN; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + + if (psres<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("poll/select failed: %s",strerror(errno)); + } + return -1; + } + if (psres==0) /* timed out */ + break; + +#ifdef NO_POLL + if (FD_ISSET(isock,&fds) || FD_ISSET(isock,&fdse)) +#else + if (pfd.revents&(POLLIN|POLLERR)) +#endif + { + char buf[1024]; + socklen_t sl=sizeof(from); + int len; + + if ((len=recvfrom(isock,&buf,sizeof(buf),0,(struct sockaddr *)&from,&sl))!=-1) { + if (len>sizeof(struct iphdr)) { + struct iphdr iph; + + memcpy(&iph, buf, sizeof(iph)); + if (len-iph.ip_hl*4>=ICMP_BASEHDR_LEN) { + struct icmphdr icmpp; + + memcpy(&icmpp, ((uint32_t *)buf)+iph.ip_hl, sizeof(icmpp)); + if (iph.ip_saddr==addr.s_addr && icmpp.icmp_type==ICMP_ECHOREPLY && + ntohs(icmpp.icmp_id)==id && ntohs(icmpp.icmp_seq)<=i) { + return (i-ntohs(icmpp.icmp_seq))*timeout+(time(NULL)-tm); /* return the number of ticks */ + } else { + /* No regular echo reply. Maybe an error? */ + if (icmp4_errcmp((char *)&icmpd, ICMP4_ECHO_LEN, &to.sin_addr, buf, len, ICMP_DEST_UNREACH) || + icmp4_errcmp((char *)&icmpd, ICMP4_ECHO_LEN, &to.sin_addr, buf, len, ICMP_TIME_EXCEEDED)) { + return -1; + } + } + } + } + } else { + return -1; /* error */ + } + } + else { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_error("Unhandled poll/select event in ping4() at %s, line %d.",__FILE__,__LINE__); + } + return -1; + } + tpassed=time(NULL)-tm; + } while (tpassedtpassed?timeout-tpassed:0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=select(isock+1,&fds,NULL,&fdse,&tv); +#else + struct pollfd pfd; + pfd.fd=isock; + pfd.events=POLLIN; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + + if (psres<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("poll/select failed: %s",strerror(errno)); + } + return -1; + } + if (psres==0) /* timed out */ + break; + +#ifdef NO_POLL + if (FD_ISSET(isock,&fds) || FD_ISSET(isock,&fdse)) +#else + if (pfd.revents&(POLLIN|POLLERR)) +#endif + { + char buf[1024]; + socklen_t sl=sizeof(from); + int len; + if ((len=recvfrom(isock,&buf,sizeof(buf),0,(struct sockaddr *)&from,&sl))!=-1) { + if (len>=sizeof(struct icmp6_hdr)) { + /* we get packets without IPv6 header, luckily */ + struct icmp6_hdr icmpp; + + memcpy(&icmpp, buf, sizeof(icmpp)); + if (IN6_ARE_ADDR_EQUAL(&from.sin6_addr,&a) && + ntohs(icmpp.icmp6_id)==id && ntohs(icmpp.icmp6_seq)<=i) { + return (i-ntohs(icmpp.icmp6_seq))*timeout+(time(NULL)-tm); /* return the number of ticks */ + } else { + /* No regular echo reply. Maybe an error? */ + if (icmp6_errcmp((char *)&icmpd, sizeof(icmpd), &from.sin6_addr, buf, len, ICMP6_DST_UNREACH) || + icmp6_errcmp((char *)&icmpd, sizeof(icmpd), &from.sin6_addr, buf, len, ICMP6_TIME_EXCEEDED)) { + return -1; + } + } + } + } else { + return -1; /* error */ + } + } + else { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_error("Unhandled poll/select event in ping6() at %s, line %d.",__FILE__,__LINE__); + } + return -1; + } + tpassed=time(NULL)-tm; + } while (tpassedipv4,timeout,rep); +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + /* If it is a IPv4 mapped IPv6 address, we prefer ICMPv4. */ + if (ping_isocket!=-1 && IN6_IS_ADDR_V4MAPPED(&addr->ipv6)) { + struct in_addr v4; + v4.s_addr=((uint32_t *)&addr->ipv6)[3]; + return ping4(v4,timeout,rep); + } else + return ping6(addr->ipv6,timeout,rep); + } +#endif + return -1; +} + +#else +# error "Huh! No OS macro defined!" +#endif /*(TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)*/ diff --git a/app/src/main/jni/pdnsd/src/icmp.h b/app/src/main/jni/pdnsd/src/icmp.h new file mode 100644 index 0000000..3fa6778 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/icmp.h @@ -0,0 +1,43 @@ +/* icmp.h - Server response tests using ICMP echo requests + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2007 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef ICMP_H +#define ICMP_H + + +#include +#include "ipvers.h" + +volatile extern int ping_isocket; +volatile extern int ping6_isocket; + +/* initialize a socket for pinging */ +void init_ping_socket(void); + +/* + * This is a classical ping routine. + * timeout in 10ths of seconds, rep is the repetition count. + */ + +int ping(pdnsd_a *addr, int timeout, int rep); + +#endif diff --git a/app/src/main/jni/pdnsd/src/ipvers.h b/app/src/main/jni/pdnsd/src/ipvers.h new file mode 100644 index 0000000..b1d7a2c --- /dev/null +++ b/app/src/main/jni/pdnsd/src/ipvers.h @@ -0,0 +1,297 @@ +/* ipvers.h - definitions for IPv4 and IPv6 + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2007, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef IPVERS_H +#define IPVERS_H + +#include +#include +#include +#include +#include +#include "rr_types.h" + +#if defined(ENABLE_IPV4) && !defined(ENABLE_IPV6) +# ifdef DEFAULT_IPV4 +# undef DEFAULT_IPV4 +# endif +# define DEFAULT_IPV4 1 +#endif + +#if !defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +# ifdef DEFAULT_IPV4 +# undef DEFAULT_IPV4 +# endif +# define DEFAULT_IPV4 0 +#endif + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +# define ELSE_IPV6 else +#else +# define ELSE_IPV6 +#endif + +/* From main.c */ +#ifdef ENABLE_IPV4 +# ifdef ENABLE_IPV6 +extern short int run_ipv4; +extern short int cmdlineipv; +# define SEL_IPVER(a4,a6) (run_ipv4? a4: a6) +# else +# define run_ipv4 1 +# define SEL_IPVER(a4,a6) (a4) +# endif +#else +# define run_ipv4 0 +# define SEL_IPVER(a4,a6) (a6) +#endif +#ifdef ENABLE_IPV6 +#define DEFAULT_IPV4_6_PREFIX "::ffff:0.0.0.0" +/* extern short int cmdlineprefix; */ +#endif + +#if (TARGET==TARGET_LINUX) && !defined(HAVE_STRUCT_IN_PKTINFO) +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +#if (TARGET==TARGET_LINUX) +/* some older glibc versions seem to lack this. */ +# ifndef IP_PKTINFO +# define IP_PKTINFO 8 +# endif +# ifndef CMSG_LEN +/* ---- from glibc 2.1.2 */ + +/* Ancillary data object manipulation macros. */ +# if !defined __STRICT_ANSI__ && defined __GNUC__ && __GNUC__ >= 2 +# define CMSG_DATA(cmsg) ((cmsg)->__cmsg_data) +# else +# define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1)) +# endif +# define CMSG_NXTHDR(mhdr, cmsg) __cmsg_nxthdr (mhdr, cmsg) +# define CMSG_FIRSTHDR(mhdr) \ + ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) \ + ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) NULL) +# define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \ + & ~(sizeof (size_t) - 1)) +# define CMSG_SPACE(len) (CMSG_ALIGN (len) \ + + CMSG_ALIGN (sizeof (struct cmsghdr))) +# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +extern struct cmsghdr *__cmsg_nxthdr __P ((struct msghdr *__mhdr, + struct cmsghdr *__cmsg)); +# ifdef __USE_EXTERN_INLINES +# ifndef _EXTERN_INLINE +# define _EXTERN_INLINE extern __inline +# endif +_EXTERN_INLINE struct cmsghdr * +__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg) __THROW +{ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) + /* The kernel header does this so there may be a reason. */ + return 0; + + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); + if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control + + __mhdr->msg_controllen) + || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) + >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) + /* No more entries. */ + return 0; + return __cmsg; +} +# endif /* Use `extern inline'. */ +/* ---- */ +# endif +#endif + +#if defined(ENABLE_IPV4) && !defined(SIN_LEN) && (TARGET==TARGET_BSD) +# define SIN_LEN +#endif + +#if defined(ENABLE_IPV6) && (TARGET==TARGET_LINUX) + +/* Some glibc versions (I know of 2.1.2) get this wrong, so we define our own. To be exact, this is fixed + * glibc code. */ +#ifdef IN6_ARE_ADDR_EQUAL +# undef IN6_ARE_ADDR_EQUAL +#endif +#define IN6_ARE_ADDR_EQUAL(a,b) \ + ((((uint32_t *) (a))[0] == ((uint32_t *) (b))[0]) && \ + (((uint32_t *) (a))[1] == ((uint32_t *) (b))[1]) && \ + (((uint32_t *) (a))[2] == ((uint32_t *) (b))[2]) && \ + (((uint32_t *) (a))[3] == ((uint32_t *) (b))[3])) + +#endif + +/* This is the IPv6 flowid that we pass on to the IPv6 protocol stack. This value was not currently defined + * at the time of writing. Should this change, define a appropriate flowinfo here. */ +#ifndef IPV6_FLOWINFO +#define IPV6_FLOWINFO 0 +#endif + +/* There does not seem to be a function/macro to generate IPv6-mapped IPv4-Adresses. So here comes mine. + * Pass an in_addr* and an in6_addr* */ +#define IPV6_MAPIPV4(a,b) {((uint32_t *)(b))[3]=(a)->s_addr; \ + ((uint32_t *)(b))[2]=htonl(0xffff); \ + ((uint32_t *)(b))[1]=((uint32_t *)(b))[0]=0; } + +/* A macro to extract the pointer to the address of a struct sockaddr (_in or _in6) */ + +#define SOCKA_A4(a) ((pdnsd_a *)&((struct sockaddr_in *)(a))->sin_addr) +#define SOCKA_A6(a) ((pdnsd_a *)&((struct sockaddr_in6 *)(a))->sin6_addr) + +#define SOCKA_A(a) SEL_IPVER(SOCKA_A4(a),SOCKA_A6(a)) +#define PDNSD_PF_INET SEL_IPVER(PF_INET,PF_INET6) +#define PDNSD_AF_INET SEL_IPVER(AF_INET,AF_INET6) + +/* This is to compare two addresses. This is a macro because it may change due to the more complex IPv6 adressing architecture + * (there are, for example, two equivalent addresses of the loopback device) + * Pass this two addresses as in_addr or in6_addr. pdnsd_a is ok (it is a union) */ + +#define ADDR_EQUIV4(a,b) (((struct in_addr *)(a))->s_addr==((struct in_addr *)(b))->s_addr) +#define ADDR_EQUIV6(a,b) IN6_ARE_ADDR_EQUAL(((struct in6_addr *)(a)),((struct in6_addr *)(b))) + +#define ADDR_EQUIV(a,b) SEL_IPVER(ADDR_EQUIV4(a,b), ADDR_EQUIV6(a,b)) + +/* Compare an IPv6 adress with an IPv4 one. b should have type struct in_addr*. + Note the similarity with the IPV6_MAPIPV4 macro. */ +#define ADDR_EQUIV6_4(a,b) (((uint32_t *)(a))[3]==(b)->s_addr && \ + ((uint32_t *)(a))[2]==htonl(0xffff) && \ + ((uint32_t *)(a))[1]==0 && \ + ((uint32_t *)(a))[0]==0) + +/* Compare two address a and b in combination with a netmask m. + Only the bits coresponding to those set in the netmask are matched, the rest are ignored. + Pass in_addr* or in6_addr* arguments, respectively. */ +#define ADDR4MASK_EQUIV(a,b,m) ((((a)->s_addr^(b)->s_addr)&(m)->s_addr)==0) +#define ADDR6MASK_EQUIV(a,b,m) (((((uint32_t *)(a))[0]^((uint32_t *)(b))[0])&((uint32_t *)(m))[0])==0 && \ + ((((uint32_t *)(a))[1]^((uint32_t *)(b))[1])&((uint32_t *)(m))[1])==0 && \ + ((((uint32_t *)(a))[2]^((uint32_t *)(b))[2])&((uint32_t *)(m))[2])==0 && \ + ((((uint32_t *)(a))[3]^((uint32_t *)(b))[3])&((uint32_t *)(m))[3])==0) + +/* See if we need 4.4BSD style sockaddr_* structures and define some macros that set the length field. + * The non-4.4BSD behaviour is the only one that is POSIX-conformant.*/ +#if defined(SIN6_LEN) || defined(SIN_LEN) +# define BSD44_SOCKA +# define SET_SOCKA_LEN4(socka) (socka.sin_len=sizeof(struct sockaddr_in)) +# define SET_SOCKA_LEN6(socka) (socka.sin6_len=sizeof(struct sockaddr_in6)) +#else +# define SET_SOCKA_LEN4(socka) +# define SET_SOCKA_LEN6(socka) +#endif + +#ifdef ENABLE_IPV6 +# define ADDRSTR_MAXLEN INET6_ADDRSTRLEN +#else +# ifdef INET_ADDRSTRLEN +# define ADDRSTR_MAXLEN INET_ADDRSTRLEN +# else +# define ADDRSTR_MAXLEN 16 +# endif +#endif + +#if (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifdef ENABLE_IPV6 +# ifndef IPV6_RECVPKTINFO +/* This appears to be needed e.g. on Darwin (Mac OS X). */ +# define IPV6_RECVPKTINFO IPV6_PKTINFO +# endif +#endif + +typedef union { +#ifdef ENABLE_IPV4 + struct in_addr ipv4; +#endif +#ifdef ENABLE_IPV6 + struct in6_addr ipv6; +#endif +} pdnsd_a; + +#ifdef ENABLE_IPV4 +#define PDNSD_A_INITIALIZER {{INADDR_ANY}} +#else +#define PDNSD_A_INITIALIZER {IN6ADDR_ANY_INIT} +#endif + + +/* The pdnsd_a2 type is very similar to pdnsd_a, but can hold + both an IPv4 and an IPv6 address at the same time, + i.e. a struct instead of a union. +*/ +typedef struct { +#ifdef ENABLE_IPV6 + struct in6_addr ipv6; +#endif + struct in_addr ipv4; +} pdnsd_a2; + +/* Macros/functions for assigning/converting a pdnsd_a* to a pdnsd_a2* type, + and vice versa. +*/ +#ifdef ENABLE_IPV6 +inline static void SET_PDNSD_A2(pdnsd_a2 *a2, pdnsd_a *a) __attribute__((always_inline)); +inline static void SET_PDNSD_A2(pdnsd_a2 *a2, pdnsd_a *a) +{ +#ifdef ENABLE_IPV4 + if(run_ipv4) + a2->ipv4=a->ipv4; + else +#endif + { + a2->ipv6=a->ipv6; + a2->ipv4.s_addr=INADDR_ANY; + } +} +#else +# define SET_PDNSD_A2(a2,a) ((a2)->ipv4=(a)->ipv4) +#endif + +#define PDNSD_A2_TO_A(a2) SEL_IPVER(((pdnsd_a *)&(a2)->ipv4),((pdnsd_a *)&(a2)->ipv6)) + +/* Do we have sufficient support in the C libraries to allow local AAAA records + to be defined? */ +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_PTON) +# define ALLOW_LOCAL_AAAA IS_CACHED_AAAA +#else +# define ALLOW_LOCAL_AAAA 0 +#endif + +/* Used to enter local records */ +typedef union { + struct in_addr ipv4; +#if ALLOW_LOCAL_AAAA + struct in6_addr ipv6; +#endif +} pdnsd_ca; + + +#endif diff --git a/app/src/main/jni/pdnsd/src/list.c b/app/src/main/jni/pdnsd/src/list.c new file mode 100644 index 0000000..0370186 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/list.c @@ -0,0 +1,171 @@ +/* list.c - Dynamic array and list handling + + Copyright (C) 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2007, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include "helpers.h" +#include "error.h" +#include "list.h" + + +/* Grow a dynamic array to hold one extra element. + This could be done using da_resize(), but this is such a common operation + it is has been given its own optimized implementation. + da_grow1() returns a pointer to the new (possibly reallocated) array if + successful, otherwise it frees the old array (after freeing all the array + elements if a clean-up routine is supplied) and returns NULL. +*/ +darray da_grow1(darray a, size_t headsz, size_t elemsz, void (*cleanuproutine) (void *)) +{ + size_t k = (a?a->nel:0); + if(!a || (k!=0 && (k&7)==0)) { + darray tmp=(darray)realloc(a, headsz+elemsz*(k+8)); + if (!tmp && a) { + if(cleanuproutine) { + size_t i; + for(i=0;inel=k+1; + return a; +} + +inline static size_t alloc_nel(size_t n) +{ + return n==0 ? 8 : (n+7)&(~7); +} + +/* da_resize() allows you to grow (or shrink) a dynamic array to an arbitrary length n, + but is otherwise similar to da_grow1(). +*/ +darray da_resize(darray a, size_t headsz, size_t elemsz, size_t n, void (*cleanuproutine) (void *)) +{ + size_t ael = (a?alloc_nel(a->nel):0); + size_t new_ael = alloc_nel(n); + if(new_ael != ael) { + /* adjust alloced space. */ + darray tmp=(darray)realloc(a, headsz+elemsz*new_ael); + if (!tmp && a) { + if(cleanuproutine) { + size_t i,k=a->nel; + for(i=0;inel=n; + return a; +} + +#ifdef ALLOC_DEBUG +void DBGda_free(darray a, size_t headsz, size_t elemsz, char *file, int line) +{ + if (a==NULL) + {DEBUG_MSG("- da_free, %s:%d, not initialized\n", file, line);} + else + {DEBUG_MSG("- da_free, %s:%d, %lu bytes\n", file, line, + (unsigned long)(headsz+elemsz*alloc_nel(a->nel)));} + free(a); +} +#endif + + +#define DLISTALIGN(len) (((len) + (sizeof(size_t)-1)) & ~(sizeof(size_t)-1)) +/* This mask corresponds to a chunk size of 1024. */ +#define DLISTCHUNKSIZEMASK ((size_t)0x3ff) + +/* Add space for a new item of size len to the list a. + dlist_grow() returns a pointer to the new (possibly reallocated) list structure if + successful, otherwise it frees the old list and returns NULL. +*/ +dlist dlist_grow(dlist a, size_t len) +{ + size_t sz=0, allocsz=0, szincr, newsz; + if(a) { + sz=a->last+a->lastsz; + allocsz = (sz+DLISTCHUNKSIZEMASK)&(~DLISTCHUNKSIZEMASK); + *((size_t *)&a->data[a->last])=a->lastsz; + } + szincr=DLISTALIGN(len+sizeof(size_t)); + newsz=sz+szincr; + if(newsz>allocsz) { + dlist tmp; + allocsz = (newsz+DLISTCHUNKSIZEMASK)&(~DLISTCHUNKSIZEMASK); + tmp=realloc(a, sizeof(struct _dynamic_list_head)+allocsz); + if (!tmp) + free(a); + a=tmp; + } + if(a) { + a->last=sz; + a->lastsz=szincr; + *((size_t *)&a->data[sz])=0; + } + return a; +} + + +/* Add a new node, capable of holding data of size len, at the end of a linked list. + llist_grow() returns 1 if successful, otherwise it frees the entire linked list + and returns 0. + */ +int llist_grow(llist *a, size_t len) +{ + struct llistnode_s *new= (struct llistnode_s *)malloc(sizeof(struct llistnode_s)+len); + + if(!new) { + llist_free(a); + return 0; + } + + new->next=NULL; + + if(!a->first) + a->first=new; + else + a->last->next=new; + + a->last=new; + + return 1; +} + +void llist_free(llist *a) +{ + struct llistnode_s *p= a->first; + + while(p) { + struct llistnode_s *next= p->next; + free(p); + p=next; + } + + a->first=NULL; + a->last= NULL; +} diff --git a/app/src/main/jni/pdnsd/src/list.h b/app/src/main/jni/pdnsd/src/list.h new file mode 100644 index 0000000..c63534e --- /dev/null +++ b/app/src/main/jni/pdnsd/src/list.h @@ -0,0 +1,170 @@ +/* list.h - Dynamic array and list handling + + Copyright (C) 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef LIST_H +#define LIST_H + +#include +#include +#include "pdnsd_assert.h" + + +typedef struct {size_t nel;} *darray; + +/* used in type declarations */ +#define DYNAMIC_ARRAY(typ) struct {size_t nel; typ elem[0];} +#define DA_TYP_OFFSET(atyp) ((size_t)((atyp)0)->elem) +#define DA_OFFSET(a) DA_TYP_OFFSET(typeof (a)) + +#define DA_CREATE(atyp,n) ((atyp)da_resize(NULL,DA_TYP_OFFSET(atyp),sizeof(((atyp)0)->elem[0]),n,NULL)) +#define DA_INDEX(a,i) ((a)->elem[i]) +/* Used often, so make special-case macro here */ +#define DA_LAST(a) ((a)->elem[(a)->nel-1]) + +#define DA_GROW1(a) ((typeof (a))da_grow1((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),NULL)) +#define DA_GROW1_F(a,cleanup) ((typeof (a))da_grow1((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),cleanup)) +#define DA_RESIZE(a,n) ((typeof (a))da_resize((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),n,NULL)) +#define DA_NEL(a) da_nel((darray)(a)) + +/* + * Some or all of these should be inline. + * They aren't macros for type safety. + */ + +darray da_grow1(darray a, size_t headsz, size_t elemsz, void (*cleanuproutine) (void *)); +darray da_resize(darray a, size_t headsz, size_t elemsz, size_t n, void (*cleanuproutine) (void *)); + +inline static unsigned int da_nel(darray a) + __attribute__((always_inline)); +inline static unsigned int da_nel(darray a) +{ + if (a==NULL) + return 0; + return a->nel; +} + +/* alloc/free debug code.*/ +#ifdef ALLOC_DEBUG +void DBGda_free(darray a, size_t headsz, size_t elemsz, char *file, int line); +#define da_free(a) DBGda_free((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]), __FILE__, __LINE__) +#else +#define da_free free +#endif + + +/* This dynamic "list" structure is useful if the items are not all the same size. + The elements can only be read back in sequential order, not indexed as with the dynamic arrays. +*/ +struct _dynamic_list_head { + size_t last,lastsz; + char data[0]; +}; + +typedef struct _dynamic_list_head *dlist; + +inline static void *dlist_first(dlist a) + __attribute__((always_inline)); +inline static void *dlist_first(dlist a) +{ + return a?&a->data[sizeof(size_t)]:NULL; +} + +/* dlist_next() returns a reference to the next item in the list, or NULL is there is no next item. + ref should be properly aligned. + If the dlist was grown with dlist_grow(), this should be OK. +*/ +inline static void *dlist_next(void *ref) + __attribute__((always_inline)); +inline static void *dlist_next(void *ref) +{ + size_t incr= *(((size_t *)ref)-1); + return incr?((char *)ref)+incr:NULL; +} + +/* dlist_last() returns a reference to the last item. */ +inline static void *dlist_last(dlist a) + __attribute__((always_inline)); +inline static void *dlist_last(dlist a) +{ + return a?&a->data[a->last+sizeof(size_t)]:NULL; +} + +dlist dlist_grow(dlist a, size_t len); + +#define dlist_free free + + +/* linked list data type. */ +struct llistnode_s { + struct llistnode_s *next; + char *data[0]; +}; + +typedef struct { + struct llistnode_s *first, *last; +} + llist; + +inline static void llist_init(llist *a) + __attribute__((always_inline)); +inline static void llist_init(llist *a) +{ + a->first=NULL; + a->last= NULL; +} + +inline static int llist_isempty(llist *a) + __attribute__((always_inline)); +inline static int llist_isempty(llist *a) +{ + return a->first==NULL; +} + +inline static void *llist_first(llist *a) + __attribute__((always_inline)); +inline static void *llist_first(llist *a) +{ + struct llistnode_s *p= a->first; + return p?p->data:NULL; +} + +inline static void *llist_next(void *ref) + __attribute__((always_inline)); +inline static void *llist_next(void *ref) +{ + struct llistnode_s *next= *(((struct llistnode_s **)ref)-1); + return next?next->data:NULL; +} + +inline static void *llist_last(llist *a) + __attribute__((always_inline)); +inline static void *llist_last(llist *a) +{ + struct llistnode_s *p= a->last; + return p?p->data:NULL; +} + +int llist_grow(llist *a, size_t len); +void llist_free(llist *a); + +#endif /* def LIST_H */ diff --git a/app/src/main/jni/pdnsd/src/main.c b/app/src/main/jni/pdnsd/src/main.c new file mode 100644 index 0000000..ad9863d --- /dev/null +++ b/app/src/main/jni/pdnsd/src/main.c @@ -0,0 +1,717 @@ +/* main.c - Command line parsing, intialisation and server start + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +/* in order to use O_NOFOLLOW on Linux: */ +/* #define _GNU_SOURCE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "consts.h" +#include "cache.h" +#include "status.h" +#include "servers.h" +#include "dns_answer.h" +#include "dns_query.h" +#include "error.h" +#include "helpers.h" +#include "icmp.h" +#include "hash.h" + + +#if DEBUG>0 +short int debug_p=0; +#endif +short int stat_pipe=0; + +/* int sigr=0; */ +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +short int cmdlineipv=0; +#endif +cmdlineflags_t cmdline={0}; +pthread_t main_thrid,servstat_thrid,statsock_thrid,tcps_thrid,udps_thrid; +uid_t init_uid; +#if DEBUG>0 +FILE *dbg_file=NULL; +#endif +volatile int tcp_socket=-1; +volatile int udp_socket=-1; +sigset_t sigs_msk; +char *conf_file=NULL; + + +/* version and licensing information */ +static const char info_message[] = + + "pdnsd - dns proxy daemon, version " VERSION "\n\n" + "Copyright (C) 2000, 2001 Thomas Moestl\n" + "Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts\n\n" + "pdnsd is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 3 of the License, or\n" + "(at your option) any later version.\n\n" + "pdnsd is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with pdsnd; see the file COPYING. If not, see\n" + ".\n"; + + +/* the help page */ +static const char help_message[] = + + "\n\nUsage: pdnsd [-h] [-V] [-s] [-d] [-g] [-t] [-p file] [-vn] [-mxx] [-c file]" +#ifdef ENABLE_IPV4 + " [-4]" +#endif +#ifdef ENABLE_IPV6 + " [-6] [-i prefix]" +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + " [-a]" +#endif + "\n\n" + "Options:\n" + "-h\t\t--or--\n" + "--help\t\tprint this help page and exit.\n" + "-V\t\t--or--\n" + "--version\tprint version and license information and exit.\n" + "--pdnsd-user\tprint the user pdnsd will run as and exit.\n" + "-s\t\t--or--\n" + "--status\tEnable status control socket in the cache directory.\n" + "-d\t\t--or--\n" + "--daemon\tStart pdnsd in daemon mode (as background process.)\n" + "-g\t\t--or--\n" + "--debug\t\tPrint some debug messages on the console or to the\n" + "\t\tfile pdnsd.debug in your cache directory (in daemon mode).\n" + "-t\t\t--or--\n" + "--tcp\t\tEnables the TCP server thread. pdnsd will then serve\n" + "\t\tTCP and UDP queries.\n" + "-p\t\tWrites the pid the server runs as to a specified filename.\n" + "\t\tWorks only in daemon mode.\n" + "-vn\t\tsets the verbosity of pdnsd. n is a numeric argument from 0\n" + "\t\t(normal operation) to 9 (many messages for debugging).\n" + "\t\tUse like -v2\n" + "-mxx\t\tsets the query method pdnsd uses. Possible values for xx are:\n" + "\t\tuo (UDP only), to (TCP only), tu (TCP or, if the server\n" + "\t\tdoes not support this, UDP) and ut (UDP and, if the reply was\n" + "\t\ttruncated, TCP). Use like -muo. Preset: " +#if M_PRESET==UDP_ONLY + "-muo" +#elif M_PRESET==TCP_ONLY + "-mto" +#elif M_PRESET==TCP_UDP + "-mtu" +#else + "-mut" +#endif + "\n" + "-c\t\t--or--\n" + "--config-file\tspecifies the file the configuration is read from.\n" +#ifdef ENABLE_IPV4 + "-4\t\tswitches to IPv4 mode.\n" + "\t\t" +# if DEFAULT_IPV4 + "On" +# else + "Off" +# endif + " by default.\n" +#endif +#ifdef ENABLE_IPV6 + "-6\t\tswitches to IPv6 mode.\n" + "\t\t" +# if DEFAULT_IPV4 + "Off" +# else + "On" +# endif + " by default.\n" + "-i\t\t--or--\n" + "--ipv4_6_prefix\tspecifies the prefix pdnsd uses to map IPv4 to IPv6\n" + "\t\taddresses. Must be a valid IPv6 address.\n" + "\t\tDefault is " DEFAULT_IPV4_6_PREFIX "\n" +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + "-a\t\tWith this option, pdnsd will try to detect automatically if\n" + "\t\tthe system supports IPv6, and revert to IPv4 otherwise.\n" +#endif + "\n\n\"no\" can be prepended to the --status, --daemon, --debug and --tcp\n" + "options (e.g. --notcp) to reverse their effect.\n"; + + +/* These are some init steps we have to call before we get daemon on linux, but need + * to call after daemonizing on other OSes. + * Theay are also the last steps before we drop privileges. */ +int final_init() +{ +#ifndef NO_TCP_SERVER + if (!global.notcp) + tcp_socket=init_tcp_socket(); +#endif + udp_socket=init_udp_socket(); + if (tcp_socket==-1 && udp_socket==-1) { + log_error("tcp and udp initialization failed. Exiting."); + return 0; + } + if (global.strict_suid) { + if (!run_as(global.run_as)) { + return 0; + } + } + return 1; +} + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +/* Check if IPv6 is available. + * With thanks to Juliusz Chroboczek. + */ +static int check_ipv6() +{ + int fd; + fd = socket(PF_INET6, SOCK_STREAM, 0); + if(fd < 0) { + if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) + return 0; + return -1; + } + close(fd); + return 1; +} +#endif + + +/* + * Argument parsing, init, server startup + */ +int main(int argc,char *argv[]) +{ + int i,sig,pfd=-1; /* Initialized to inhibit compiler warning */ + + main_thrid=pthread_self(); + servstat_thrid=main_thrid; + statsock_thrid=main_thrid; + tcps_thrid=main_thrid; + udps_thrid=main_thrid; + init_uid=getuid(); +#ifdef ENABLE_IPV6 + { + int err; + if((err=inet_pton(AF_INET6,DEFAULT_IPV4_6_PREFIX,&global.ipv4_6_prefix))<=0) { + fprintf(stderr,"Error: inet_pton() wont accept default prefix %s in %s, line %d\n", + DEFAULT_IPV4_6_PREFIX,__FILE__,__LINE__); + if(err) + perror("inet_pton"); + exit(1); + } + } +#endif + + /* Parse the command line. + Remember which options were specified here, because the command-line options + shall override the ones given in the config file */ + for (i=1;ipw_name); + else + printf("%i\n",uid); + } + exit(0); + } + + if(!global.cache_dir) { + fprintf(stderr,"Error: cache_dir is unset\n"); + exit(1); + } + if(!global.scheme_file) global.scheme_file = "/var/lib/pcmcia/scheme"; + stat_pipe=global.stat_pipe; + + if (!(global.run_as[0] && global.strict_suid)) { + for (i=0; iuptest==C_EXEC && sp->uptest_usr[0]=='\0') { + uid_t uid=getuid(); + struct passwd *pws=getpwuid(uid); + + /* No explicit uptest user given. If we run_as and strict_suid, we assume that + * this is safe. If not - warn. */ + fprintf(stderr,"Warning: uptest command \"%s\" will implicitly be executed as user ", sp->uptest_cmd); + if (pws) + fprintf(stderr,"%s\n",pws->pw_name); + else + fprintf(stderr,"%i\n",uid); + + } + } + } + + if (global.daemon && global.pidfile) { + if (unlink(global.pidfile)!=0 && errno!=ENOENT) { + log_error("Error: could not unlink pid file %s: %s",global.pidfile, strerror(errno)); + exit(1); + } + if ((pfd=open(global.pidfile,O_WRONLY|O_CREAT|O_EXCL +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#else + /* + * No O_NOFOLLOW. Nevertheless, this not a hole, since the + * directory for pidfiles should not be world writeable. + * OS's that do not support O_NOFOLLOW are currently not + * supported, this is just-in-case code. + */ +#endif + , 0600))==-1) + { + log_error("Error: could not open pid file %s: %s",global.pidfile, strerror(errno)); + exit(1); + } + } + for (i=0;i0 + if (global.debug) { + char dbgpath[strlen(global.cache_dir)+sizeof("/pdnsd.debug")]; + stpcpy(stpcpy(dbgpath,global.cache_dir),"/pdnsd.debug"); + if (!(dbg_file=fopen(dbgpath,"w"))) + log_warn("Warning: could not open debug file %s: %s",dbgpath, strerror(errno)); + } +#endif + } else { +#if DEBUG>0 + dbg_file=stdout; +#endif + } + +#if DEBUG>0 + debug_p= (global.debug && dbg_file); +#endif + log_info(0,"pdnsd-%s starting.\n",VERSION); + DEBUG_MSG("Debug messages activated\n"); + +#if (TARGET!=TARGET_LINUX) + if (!final_init()) + _exit(1); +#endif + DEBUG_MSG(SEL_IPVER("Using IPv4.\n", "Using IPv6.\n")); + + /* initialize attribute for creating detached threads */ + pthread_attr_init(&attr_detached); + pthread_attr_setdetachstate(&attr_detached,PTHREAD_CREATE_DETACHED); + + read_disk_cache(); + + /* This must be done before any other thread is started to avoid races. */ + if (stat_pipe) + init_stat_sock(); + + + /* Before this point, logging and cache accesses are not locked because we are single-threaded. */ + init_log_lock(); + init_cache_lock(); + + sigemptyset(&sigs_msk); + sigaddset(&sigs_msk,SIGHUP); + sigaddset(&sigs_msk,SIGINT); +#ifndef THREADLIB_NPTL + sigaddset(&sigs_msk,SIGILL); +#endif + sigaddset(&sigs_msk,SIGABRT); + sigaddset(&sigs_msk,SIGFPE); +#ifndef THREADLIB_NPTL + sigaddset(&sigs_msk,SIGSEGV); +#endif + sigaddset(&sigs_msk,SIGTERM); + /* if (!daemon_p) { + sigaddset(&sigs_msk,SIGQUIT); + } */ +#if (TARGET==TARGET_LINUX) + pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); +#endif + +#if DEBUG>0 + { + int err; + /* Generate a key for storing our thread id's */ + if ((err=pthread_key_create(&thrid_key, NULL)) != 0) { + log_error("pthread_key_create failed: %s",strerror(err)); + _exit(1); + } + } +#endif + + { +#if DEBUG>0 + int thrdsucc=1; +# define thrdfail (thrdsucc=0) +#else +# define thrdfail +#endif + + if(start_servstat_thread()) thrdfail; + +#if (TARGET==TARGET_LINUX) + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + _exit(1); + } + } +#endif + + if (stat_pipe) + if(start_stat_sock()) thrdfail; + + start_dns_servers(); + +#if DEBUG>0 + if(thrdsucc) { + DEBUG_MSG("All threads started successfully.\n"); + } +#endif +#undef thrdfail + } + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) + pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); + waiting=1; +#endif + { + int err; + while ((err=sigwait(&sigs_msk,&sig))) { + if (err!=EINTR) { + log_error("sigwait failed: %s",strerror(err)); + sig=0; + break; + } + } + } + if(sig) DEBUG_MSG("Signal %i caught.\n",sig); + write_disk_cache(); + destroy_cache(); + if(sig) log_warn("Caught signal %i. Exiting.",sig); + if (sig==SIGSEGV || sig==SIGILL || sig==SIGBUS) + crash_msg("This is a fatal signal probably triggered by a bug."); + if (ping_isocket!=-1) + close(ping_isocket); +#ifdef ENABLE_IPV6 + if (ping6_isocket!=-1) + close(ping6_isocket); +#endif + /* Close and delete the status socket */ + if(stat_pipe) close(stat_sock); + if (sock_path && unlink(sock_path)) + log_warn("Failed to unlink %s: %s",sock_path, strerror(errno)); + + free_rng(); +#if DEBUG>0 + if (debug_p && global.daemon) + if(fclose(dbg_file)<0) { + log_warn("Could not close debug file: %s", strerror(errno)); + } +#endif + _exit(0); +} diff --git a/app/src/main/jni/pdnsd/src/make_rr_types_h.pl b/app/src/main/jni/pdnsd/src/make_rr_types_h.pl new file mode 100644 index 0000000..c8c0f9f --- /dev/null +++ b/app/src/main/jni/pdnsd/src/make_rr_types_h.pl @@ -0,0 +1,309 @@ +#!/usr/bin/perl -w + +# This Perl script is used to generate rr_types.h, using rr_types.in as input. +# +# Copyright (C) 2010, 2011 Paul A. Rombouts +# +# This file is part of the pdnsd package. +# + +use strict; +use integer; + +print << "END-OF-TEXT"; +/* This file was generated by running '$0 @ARGV'. + Modifications to this file may be lost the next time it is automatically + regenerated. +*/ + +END-OF-TEXT + +my %valdic; +my %namedic; +my %classdic; +my %muset; +my $nrr=0; +my $nmu=0; +my $minval; +my $maxval; +#my $maxmuval; + +while(<>) { + if(/\S/ && !/^\s*\#/) { + if(/^\s*(?:([*+-])\s*)?([\w-]+)\s+(\d+)\s+(?:\((\w+)\))?/) { + my $mu = $1; my $name=$2; my $val=$3+0; my $class=$4; + $name =~ s/-/_/g; + if(defined($valdic{$name})) {warn "The name \"$name\" does not have a unique value.\n"} + if(defined($namedic{$val})) {warn "The value \"$val\" does not have a unique name.\n"} + $valdic{$name}=$val; $namedic{$val}=$name; $classdic{$val}=$class if defined($class); + if(defined($mu)) { + if($mu eq '-') {next} + $muset{$val}= 1; + ++$nmu; + #if(!defined($maxmuval) || $val>$maxmuval) {$maxmuval=$val} + } + else {$muset{$val}= 0} + ++$nrr; + if(!defined($minval) || $val<$minval) {$minval=$val} + if(!defined($maxval) || $val>$maxval) {$maxval=$val} + } + else {die "Can't find name-value pair in following line:\n$_\n"} + } +} + +defined($minval) or die "No values defined.\n"; +if($nrr>255) {warn "Warning: total number of cache-able RR types is greater than 255.\n"} + +print << 'END-OF-TEXT'; +/* rr_types.h - A header file with names & descriptions of + all rr types known to pdnsd + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef _RR_TYPES_H_ +#define _RR_TYPES_H_ + +#include + +END-OF-TEXT + +print "#define T_MIN $minval\n"; +foreach my $name (sort {$valdic{$a} <=> $valdic{$b} } (keys %valdic)) { + printf("#define %-12s %2d\n", "T_$name", $valdic{$name}); +} +print "#define T_MAX $maxval\n"; +print("\n/* T_MAX - T_MIN + 1 */\n","#define T_NUM ",$maxval+1-$minval,"\n"); +#print("\n/* Largest most frequently used type value. */\n","#define T_MAXMU $maxmuval\n"); +print("\n/* Number of most frequently used rr types. */\n","#define NRRMU $nmu\n"); +print("\n/* Number of remaining rr types. */\n","#define NRREXT ",$nrr-$nmu,"\n"); +print("\n/* NRRMU + NRREXT */\n","#define NRRTOT $nrr\n"); + +print << 'END-OF-TEXT'; + +/* Lookup table for converting rr type values to internally used indices. */ +extern const unsigned short int rrlkuptab[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrlkuptab[T_NUM] = { +END-OF-TEXT +my @rrtpval=(); +for(my $val=$minval, my $i=0, my $j=$nmu, my $k=$nrr; $val<=$maxval; ++$val) { + my $idx; + if(defined($muset{$val})) { + if($muset{$val}) { + $idx = $i++; + } + else { + $idx = $j++; + } + $rrtpval[$idx]=$val; + } + else { + $idx = $k++; + } + + printf('%4d', $idx); + if(defined($namedic{$val})) { + print " /* $namedic{$val} */"; + } + print ',' if $val<$maxval; + print "\n"; +} +#print << 'END-OF-TEXT'; +#}; +##endif +# +#/* Table for converting internally used indices to rr type values. +# This is more or less the inverse of the rrlkuptab[] mapping. */ +#extern const unsigned short int rrtpval[NRRTOT]; +##if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +#const unsigned short int rrtpval[NRRTOT] = { +#END-OF-TEXT +#for(my $i=0; $i<$nrr; ++$i) { +# if($i ==0) { +# print " /* Most frequently used types. */\n"; +# } +# else { +# print ",\n"; +# } +# print " /* Remaining (less frequently used) types. */\n" +# if $i == $nmu; +# my $val= $rrtpval[$i]; +# print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); +#} +print << 'END-OF-TEXT'; +}; +#endif + +/* List of most frequently used RR types in ascending order. */ +extern const unsigned short int rrmuiterlist[NRRMU]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrmuiterlist[NRRMU] = { +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val}) && $muset{$val}) { + print ",\n" if $i++; + print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); + } +} +print << 'END-OF-TEXT'; + +}; +#endif + +/* List of the cache-able RR types in ascending order. */ +extern const unsigned short int rrcachiterlist[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS +const unsigned short int rrcachiterlist[NRRTOT] = { +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + print ",\n" if $i++; + print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); + } +} +print << 'END-OF-TEXT'; + +}; +#endif + +/* Optimized getrrset macros for fixed rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0, my $j=0; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + my $name = $namedic{$val}; + my $mdef; + if($muset{$val}) { + $mdef= "GET_RRSMU(cent,$i)"; + ++$i; + } + else { + $mdef= "GET_RRSEXT(cent,$j)"; + ++$j; + } + printf("#define %-25s %s\n", "getrrset_$name(cent)", $mdef) + if defined($name); + } +} +print << 'END-OF-TEXT'; + +/* have_rr macros for fixed rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0, my $j=0; $val<=$maxval; ++$val) { + my $name = $namedic{$val}; + my $mdef = '0'; + if(defined($muset{$val})) { + if($muset{$val}) { + $mdef= "HAVE_RRMU(cent,$i)"; + ++$i; + } + else { + $mdef= "HAVE_RREXT(cent,$j)"; + ++$j; + } + } + printf("#define %-25s %s\n", "have_rr_$name(cent)", $mdef) + if defined($name); +} +print << 'END-OF-TEXT'; + +/* These macros specify which RR types are cached by pdnsd. */ +END-OF-TEXT +for(my $val=$minval; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + my $name = $namedic{$val}; + printf("#define IS_CACHED_%-10s 1\n", defined($name)? $name: "TYPE$val") + } +} +print << 'END-OF-TEXT'; + +/* Array indices for most frequently used rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val}) && $muset{$val}) { + printf("#define %-18s %2d\n", "RRMUINDEX_$namedic{$val}", $i) + if defined($namedic{$val}); + ++$i; + } +} +print << 'END-OF-TEXT'; + +/* Table of rr names. */ +extern const char *const rrnames[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS +const char *const rrnames[T_NUM] = { +END-OF-TEXT +for(my $val=$minval; $val<=$maxval; ++$val) { + my $name = $namedic{$val}; + print(' "', defined($name)? $name: "TYPE$val", '"'); + print ',' if $val<$maxval; + print "\n"; +} +print << 'END-OF-TEXT'; +}; +#endif + +/* Structure for rr information */ +struct rr_infos { + unsigned short class; /* class (values see below) */ + unsigned short excludes; /* relations to other classes. + Mutual exclusion is marked by or'ing the + respective RRCL value in this field. + Exclusions should be symmetric. */ +}; + +/* Class values */ +#define RRCL_ALIAS 1 /* for CNAMES, conflicts with RRCL_RECORD */ +#define RRCL_RECORD 2 /* normal direct record */ +#define RRCL_IDEM 4 /* types that conflict with no others (MX, CNAME, ...) */ +#define RRCL_PTR 8 /* PTR */ + +/* Standard excludes for the classes */ +#define RRX_ALIAS (RRCL_RECORD|RRCL_PTR) +#define RRX_RECORD (RRCL_ALIAS|RRCL_PTR) +#define RRX_IDEM 0 +#define RRX_PTR (RRCL_ALIAS|RRCL_RECORD) + +/* There could be a separate table detailing the relationship of types, but this + * is slightly more flexible, as it allows a finer granularity of exclusion. Also, + * Membership in multiple classes could be added. + * Index by internally used RR-set indices, not RR type values! + */ +extern const struct rr_infos rr_info[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const struct rr_infos rr_info[NRRTOT] = { +END-OF-TEXT +for(my $i=0; $i<$nrr; ++$i) { + print ",\n" if $i; + my $val=$rrtpval[$i]; + my $class = (defined($classdic{$val})? $classdic{$val}: 'IDEM'); + printf(' %-16s %-15s %s',"{RRCL_$class,", "RRX_$class}", defined($namedic{$val})?"/* $namedic{$val} */":""); +} +print << 'END-OF-TEXT'; + +}; +#endif + +int rr_tp_byname(char *name); +const char *loc2str(const void *binary, char *ascii, size_t asclen); + +#endif +END-OF-TEXT + +exit diff --git a/app/src/main/jni/pdnsd/src/netdev.c b/app/src/main/jni/pdnsd/src/netdev.c new file mode 100644 index 0000000..bd5f8c4 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/netdev.c @@ -0,0 +1,363 @@ +/* netdev.c - Test network devices for existence and status + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * Portions are under the following copyright and taken from FreeBSD + * (clause 3 deleted as it no longer applies): + * + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/net/if.h,v 1.58.2.1 2000/05/05 13:37:04 jlemon Exp $ + */ + +#include +#include "ipvers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "helpers.h" +#include "netdev.h" +#include "error.h" + + +#if (TARGET==TARGET_BSD) +/* Taken from FreeBSD net/if.h rev. 1.58.2.1 */ +#define SIZEOF_ADDR_IFREQ(ifr) \ + ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ + (sizeof(struct ifreq) - sizeof(struct sockaddr) + \ + (ifr).ifr_addr.sa_len) : sizeof(struct ifreq)) +#elif (TARGET==TARGET_CYGWIN) +#define SIZEOF_ADDR_IFREQ(ifr) (sizeof(struct sockaddr)) +#endif + +#define MAX_SOCKETOPEN_ERRS 10 +static volatile unsigned long socketopen_errs=0; + +/* + * Portions of the following code are Linux/FreeBSD specific. + * Please write interface-detection routines for other flavours of Unix if you can and want. + */ + +#if (TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +# if (TARGET==TARGET_LINUX) + +static volatile unsigned long isdn_errs=0; + +# ifdef ISDN_SUPPORT + +/* + * Test the status of an ippp interface. Taken from the isdn4k-utils (thanks!) and adapted + * by me (I love free software!) + * This will not work with older kernels. + * If your kernel is too old or too new, just try to get the status as uptest=exec command + * This will work, although slower. + */ + +# include + +int statusif(char *name) +{ + isdn_net_ioctl_phone phone; + int isdninfo,rc=0; + + if ((isdninfo = open("/dev/isdninfo", O_RDONLY))<0) { + if (++isdn_errs<=2) { + log_warn("Could not open /dev/isdninfo for uptest: %s",strerror(errno)); + } + return 0; + } + + strncp(phone.name, name, sizeof(phone.name)); + if (ioctl(isdninfo, IIOCNETGPN, &phone)==0) + rc=1; + close(isdninfo); + return rc; +} +# endif + +/* + * Test whether the network interface specified in ifname and its + * associated device specified in devname have locks owned by the + * same process. + */ +int dev_up(char *ifname, char *devname) +{ + FILE *fd; + int pidi, pidd, rv; + + { + char path[sizeof("/var/run/.pid")+strlen(ifname)]; + stpcpy(stpcpy(stpcpy(path,"/var/run/"),ifname),".pid"); + if ((fd=fopen(path, "r")) == NULL ) + return 0; + + if (fscanf(fd, "%d", &pidi) != 1 ) { + fclose(fd) ; + return 0; + } + fclose(fd); + } + + { + char path[sizeof("/var/lock/LCK..")+strlen(devname)]; + stpcpy(stpcpy(path,"/var/lock/LCK.."),devname); + if ((fd=fopen(path, "r")) == NULL) + return 0; + + if (fscanf(fd, "%d", &pidd) != 1) { + fclose(fd); + return 0; + } + fclose(fd); + } + + if (pidi != pidd) + return 0; + /* Test whether pppd is still alive */ + rv=kill(pidi,0); + return (rv==0 || (rv==-1 && errno!=ESRCH)); +} + + +# endif /*(TARGET==TARGET_LINUX)*/ + +/* + * Test whether the network device specified in devname is up and + * running (returns -1) or non-existent, down or not-running (returns 0) + * + * Note on IPv6-Comptability: rfc2133 requires all IPv6 implementation + * to be backwards-compatible to IPv4 in means of permitting socket(PF_INET,...) + * and similar. So, I don't put code here for both IPv4 and IPv6, since + * I use that socket only for ioctls. If somebody notices incompatabilities, + * please notify me. + */ +int if_up(char *devname) +{ + int sock; + struct ifreq ifr; +# if (TARGET==TARGET_LINUX) + unsigned int devnamelen=strlen(devname); + if (devnamelen>4 && devnamelen<=6 && strncmp(devname,"ippp",4)==0) { + /* This function didn't manage the interface uptest correctly. Thanks to + * Joachim Dorner for pointing out. + * The new code (statusif()) was shamelessly stolen from isdnctrl.c of the + * isdn4k-utils. */ +# ifdef ISDN_SUPPORT + return statusif(devname); +# else + if (isdn_errs++==0) { + log_warn("An ippp? device was specified for uptest, but pdnsd was compiled without ISDN support."); + log_warn("The uptest result will be wrong."); + } +# endif + /* If it doesn't match our rules for isdn devices, treat as normal if */ + } +# endif + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in if_up(): %s",strerror(errno)); + } + return 0; + } + strncp(ifr.ifr_name,devname,IFNAMSIZ); + if (ioctl(sock,SIOCGIFFLAGS,&ifr)==-1) { + close(sock); + return 0; + } + close(sock); + return (ifr.ifr_flags&IFF_UP) && (ifr.ifr_flags&IFF_RUNNING); +} + +# if (TARGET==TARGET_LINUX) +# ifdef ENABLE_IPV6 +#define MAX_IF_INET6_OPEN_ERRS 10 +static volatile unsigned long if_inet6_open_errs=0; +# endif + +int is_local_addr(pdnsd_a *a) +{ + int res=0; + +# ifdef ENABLE_IPV4 + if (run_ipv4) { + int i,sock; + struct ifreq ifr; + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + for (i=1;i<255;i++) { + ifr.ifr_ifindex=i; + if (ioctl(sock,SIOCGIFNAME,&ifr)==-1) { + /* There may be gaps in the interface enumeration, so just continue */ + continue; + } + if (ioctl(sock,SIOCGIFADDR, &ifr)==-1) { + continue; + } + if (((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr==a->ipv4.s_addr) { + res=1; + break; + } + } + close(sock); + } + +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + char buf[40]; + FILE *f; + struct in6_addr b; + /* the interface configuration and information retrieval is obiously currently done via + * rt-netlink sockets. I think it is relatively likely to change in an incompatible way the + * Linux kernel (there seem to be some major changes for 2.4). + * Right now, I just analyze the /proc/net/if_inet6 entry. This may not be the fastest, but + * should work and is easy to adapt should the format change. */ + if (!(f=fopen("/proc/net/if_inet6","r"))) { + if(++if_inet6_open_errs<=MAX_IF_INET6_OPEN_ERRS) { + log_warn("Could not open /proc/net/if_inet6 in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + /* The address is at the start of the line. We just read 32 characters and insert a ':' 7 + * times. Such, we can use inet_pton conveniently. More portable, that. */ + for(;;) { + int i,ch; char *p=buf; + for (i=0;i<32;i++) { + if(i && i%4==0) *p++ = ':'; + if ((ch=fgetc(f))==EOF) + goto fclose_return; /* we are at the end of the file and haven't found anything.*/ + if(ch=='\n') goto nextline; + *p++ = ch; + } + *p=0; + if (inet_pton(AF_INET6,buf,&b) >0) { + if (IN6_ARE_ADDR_EQUAL(&b,&a->ipv6)) { + res=1; + goto fclose_return; + } + } + do { + if ((ch=fgetc(f))==EOF) goto fclose_return; + } while(ch!='\n'); + nextline:; + } + fclose_return: + fclose(f); + } +# endif + return res; +} + +# else /*(TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)*/ + + +#define MAX_SIOCGIFCONF_ERRS 4 +static volatile unsigned long siocgifconf_errs=0; + +int is_local_addr(pdnsd_a *a) +{ + int retval=0, sock, cnt; + struct ifconf ifc; + char buf[2048]; + + + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + + ifc.ifc_len=sizeof(buf); + ifc.ifc_buf=buf; + if (ioctl(sock,SIOCGIFCONF,&ifc)==-1) { + if(++siocgifconf_errs<=MAX_SIOCGIFCONF_ERRS) { + log_warn("ioctl() call with request SIOCGIFCONF failed in is_local_addr(): %s",strerror(errno)); + } + goto close_sock_return; + } + + cnt=0; + while(cnt+sizeof(struct ifreq)<=ifc.ifc_len) { + struct ifreq *ir= (struct ifreq *)(buf+cnt); + cnt += SIZEOF_ADDR_IFREQ(*ir); + if (cnt>ifc.ifc_len) + break; + if (SEL_IPVER(ir->ifr_addr.sa_family==AF_INET && + ((struct sockaddr_in *)&ir->ifr_addr)->sin_addr.s_addr==a->ipv4.s_addr, + ir->ifr_addr.sa_family==AF_INET6 && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)&ir->ifr_addr)->sin6_addr, + &a->ipv6))) + { + retval=1; + break; + } + } + + close_sock_return: + close(sock); + + return retval; +} + +# endif + +#else +# error "Huh. No OS macro defined." +#endif diff --git a/app/src/main/jni/pdnsd/src/netdev.h b/app/src/main/jni/pdnsd/src/netdev.h new file mode 100644 index 0000000..529b8a8 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/netdev.h @@ -0,0 +1,32 @@ +/* netdev.h - Test network devices for existence and status + Copyright (C) 2000 Thomas Moestl + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _NETDEV_H_ +#define _NETDEV_H_ + +#include +#include "ipvers.h" + +int if_up(char *devname); +int dev_up(char *ifname, char *devname); +int is_local_addr(pdnsd_a *a); + +#endif diff --git a/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am b/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am new file mode 100644 index 0000000..bcb7327 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am @@ -0,0 +1,18 @@ + +sbin_PROGRAMS = pdnsd-ctl + +pdnsd_ctl_SOURCES = pdnsd-ctl.c +pdnsd_ctl_LDADD = rr_types.o +pdnsd_ctl_DEPENDENCIES = rr_types.o + +# These are Symlinks we want to have in the package +#EXTRA_DIST = rr_types.h + +pdnsd-ctl.o rr_types.o: ../rr_types.h + +../rr_types.h: ../make_rr_types_h.pl ../rr_types.in + perl ../make_rr_types_h.pl ../rr_types.in > ../rr_types.h + +rr_types.o: %.o: ../%.c + $(COMPILE) -DCLIENT_ONLY -c $< + diff --git a/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in b/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in new file mode 100644 index 0000000..80f3987 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in @@ -0,0 +1,470 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +sbin_PROGRAMS = pdnsd-ctl$(EXEEXT) +subdir = src/pdnsd-ctl +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am_pdnsd_ctl_OBJECTS = pdnsd-ctl.$(OBJEXT) +pdnsd_ctl_OBJECTS = $(am_pdnsd_ctl_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(pdnsd_ctl_SOURCES) +DIST_SOURCES = $(pdnsd_ctl_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +pdnsd_ctl_SOURCES = pdnsd-ctl.c +pdnsd_ctl_LDADD = rr_types.o +pdnsd_ctl_DEPENDENCIES = rr_types.o +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/pdnsd-ctl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/pdnsd-ctl/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +pdnsd-ctl$(EXEEXT): $(pdnsd_ctl_OBJECTS) $(pdnsd_ctl_DEPENDENCIES) + @rm -f pdnsd-ctl$(EXEEXT) + $(LINK) $(pdnsd_ctl_OBJECTS) $(pdnsd_ctl_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-ctl.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-sbinPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-sbinPROGRAMS + + +# These are Symlinks we want to have in the package +#EXTRA_DIST = rr_types.h + +pdnsd-ctl.o rr_types.o: ../rr_types.h + +../rr_types.h: ../make_rr_types_h.pl ../rr_types.in + perl ../make_rr_types_h.pl ../rr_types.in > ../rr_types.h + +rr_types.o: %.o: ../%.c + $(COMPILE) -DCLIENT_ONLY -c $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c b/app/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c new file mode 100644 index 0000000..0840c20 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c @@ -0,0 +1,802 @@ +/* pdnsd-ctl.c - Control pdnsd through a pipe + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include /* for offsetof */ +#include "../helpers.h" +#include "../status.h" +#include "../conff.h" +#include "../list.h" +#include "../dns.h" +#include "../rr_types.h" +#include "../cache.h" + +#if !defined(HAVE_ALLOCA) && !defined(alloca) +#define alloca malloc +#endif + + +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_PTON) +# define ALLOW_AAAA IS_CACHED_AAAA +#else +# define ALLOW_AAAA 0 +#endif + +static short int verbose=1; + +typedef struct { + char *name; + int val; +} cmd_s; + +#define CMD_LIST_RRTYPES (CTL_MAX+1) +#define CMD_HELP (CTL_MAX+2) +#define CMD_VERSION (CTL_MAX+3) + +static const cmd_s top_cmds[]={ + {"help",CMD_HELP},{"version",CMD_VERSION},{"list-rrtypes",CMD_LIST_RRTYPES}, + {"status",CTL_STATS},{"server",CTL_SERVER},{"record",CTL_RECORD}, + {"source",CTL_SOURCE},{"add",CTL_ADD},{"neg",CTL_NEG}, + {"config",CTL_CONFIG},{"include",CTL_INCLUDE},{"eval",CTL_EVAL}, + {"empty-cache",CTL_EMPTY}, {"dump",CTL_DUMP}, + {NULL,0} +}; +static const cmd_s server_cmds[]= {{"up",CTL_S_UP},{"down",CTL_S_DOWN},{"retest",CTL_S_RETEST},{NULL,0}}; +static const cmd_s record_cmds[]= {{"delete",CTL_R_DELETE},{"invalidate",CTL_R_INVAL},{NULL,0}}; +static const cmd_s onoff_cmds[]= {{"off",0},{"on",1},{NULL,0}}; +static const cmd_s rectype_cmds[]= {{"a",T_A}, +#if ALLOW_AAAA + {"aaaa",T_AAAA}, +#endif + {"ptr",T_PTR},{"cname",T_CNAME},{"mx",T_MX},{"ns",T_NS},{NULL,0}}; + +static const char version_message[] = + "pdnsd-ctl, version pdnsd-" VERSION "\n\n"; + +static const char license_statement[] = + "Copyright (C) 2000, 2001 Thomas Moestl\n" + "Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 Paul A. Rombouts\n\n" + "This program is part of the pdnsd package.\n\n" + "pdnsd is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 3 of the License, or\n" + "(at your option) any later version.\n\n" + "pdnsd is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with pdsnd; see the file COPYING. If not, see\n" + ".\n"; + +static const char *const help_messages[] = +{ + "Usage: pdnsd-ctl [-c cachedir] [-q] [arguments]\n\n" + + "Command-line options:\n" + + "-c\tcachedir\n\tSet the cache directory to cachedir (must match pdnsd setting).\n" + "-q\n\tBe quiet unless output is specified by command or something goes wrong.\n\n" + + "Commands and needed arguments are:\n" + + "help\t[no arguments]\n\tPrint this help.\n" + "version\t[no arguments]\n\tPrint version and license info.\n", + + "status\t[no arguments]\n\tPrint pdnsd's status.\n", + + "server\t(index|label)\t(up|down|retest)\t[dns1[,dns2[,...]]]\n" + "\tSet the status of the server with the given index to up or down, or\n" + "\tforce a retest. The index is assigned in the order of definition in\n" + "\tpdnsd.conf starting with 0. Use the status command to see the indexes.\n" + "\tYou can specify the label of a server (that matches the label option)\n" + "\tinstead of an index to make this easier.\n" + + "\tYou can specify all instead of an index to perform the action for all\n" + "\tservers registered with pdnsd.\n" + + "\tAn optional third argument can be given consisting of a list of IP\n" + "\taddresses separated by commas or spaces. This list will replace the\n" + "\taddresses of name servers used by pdnsd for the given server section.\n" + "\tThis feature is useful for run-time configuration of pdnsd with dynamic\n" + "\tDNS data in scripts called by ppp or DHCP clients. The last argument\n" + "\tmay also be an empty string, which causes existing IP addresses to be\n" + "\tremoved and the corresponding server section to become inactive.\n", + + "record\tname\t(delete|invalidate)\n" + "\tDelete or invalidate the record of the given domain if it is in the\n" + "\tcache.\n", + + "source\tfn\towner\t[ttl]\t[(on|off)]\t[noauth]\n" + "\tLoad a hosts-style file. Works like using the pdnsd source\n" + "\tconfiguration section.\n" + "\tOwner and ttl are used as in the source section. ttl has a default\n" + "\tof 900 (it does not need to be specified). The next to last argument\n" + "\tcorresponds to the serve_aliases option, and is off by default.\n" + "\tnoauth is used to make the domains non-authoritative (please\n" + "\tconsult the pdnsd manual for what that means).\n" + "\tfn is the name of the file, which must be readable by pdnsd.\n", + + "add\ta\taddr\tname\t[ttl]\t[noauth]\n" +#if ALLOW_AAAA + "add\taaaa\taddr\tname\t[ttl]\t[noauth]\n" +#endif + "add\tptr\thost\tname\t[ttl]\t[noauth]\n" + "add\tcname\thost\tname\t[ttl]\t[noauth]\n" + "add\tmx\thost\tname\tpref\t[ttl]\t[noauth]\n" + "add\tns\thost\tname\t[ttl]\t[noauth]\n" + "\tAdd a record of the given type to the pdnsd cache, replacing existing\n" + "\trecords for the same name and type. The 2nd argument corresponds\n" + "\tto the value of the option in the rr section that is named like\n" + "\tthe first argument. The addr argument may be a list of IP addresses,\n" + "\tseparated by commas or white space. The ttl is optional, the default is\n" + "\t900 seconds. noauth is used to make the domains non-authoritative.\n" + "\tIf you want no other record than the newly added in the cache, do\n" + "\tpdnsdctl record delete\n" + "\tbefore adding records.\n", + + "neg\tname\t[type]\t[ttl]\n" + "\tAdd a negatively cached record to pdnsd's cache, replacing existing\n" + "\trecords for the same name and type. If no type is given, the whole\n" + "\tdomain is cached negatively. For negatively cached records, errors are\n" + "\timmediately returned on a query, without querying other servers first.\n" + "\tThe ttl is optional, the default is 900 seconds.\n", + + "config\t[filename]\n" + "\tReload pdnsd's configuration file.\n" + "\tThe config file must be owned by the uid that pdnsd had when it was\n" + "\tstarted, and be readable by pdnsd's run_as uid. If no file name is\n" + "\tspecified, the config file used at start up is reloaded.\n", + + "include\tfilename\n" + "\tParse the given file as an include file, which may contain the same\n" + "\ttype of sections as a config file, expect for global and server\n" + "\tsections, which are not allowed. This command can be used to add data\n" + "\tto the cache without reconfiguring pdnsd.\n", + + "eval\tstring\n" + "\tParse string as if it were part of pdnsd's configuration file.\n" + "\tThe string should hold one or more complete configuration sections,\n" + "\tbut no global and server sections, which are not allowed.\n" + "\tIf multiple strings are given, they will be joined using newline chars\n" + "\tand parsed together.\n", + + "empty-cache\t[[+|-]name ...]\n" + "\tDelete all entries in the cache matching include/exclude rules.\n" + "\tIf no arguments are provided, the cache is completely emptied,\n" + "\tfreeing all existing entries. This also removes \"local\" records,\n" + "\tas defined by the config file. To restore local records, run\n" + "\t\"pdnsd-ctl config\" or \"pdnsd-ctl include filename\" immediately\n" + "\tafterwards.\n" + "\tIf one or more arguments are provided, these are interpreted as \n" + "\tinclude/exclude names. If an argument starts with a '+' the name is to\n" + "\tbe included. If an argument starts with a '-' it is to be excluded.\n" + "\tIf an argument does not begin with '+' or '-', a '+' is assumed.\n" + "\tIf the domain name of a cache entry ends in one of the names in the\n" + "\tlist, the first match will determine what happens. If the matching name\n" + "\tis to be included, the cache entry is deleted, otherwise it remains.\n" + "\tIf there are no matches, the default action is not to delete.\n", + + "dump\t[name]\n" + "\tPrint information stored in the cache about name.\n" + "\tIf name begins with a dot and is not the root domain, information\n" + "\tabout the names in the cache ending in name (including name without\n" + "\tthe leading dot) will be printed. If name is missing, information about\n" + "\tall the names in the cache will be printed.\n", + + "list-rrtypes\t[no arguments]\n" + "\tList available rr types for the neg command. Note that those are only\n" + "\tused for the neg command, not for add!\n" +}; + +#define NUM_HELP_MESSAGES (sizeof(help_messages)/sizeof(char*)) + + +/* Open connection to control socket and send command code. + If successful, open_sock returns a file descriptor for the new socket, + otherwise the program is aborted. +*/ +static int open_sock(const char *cache_dir, uint16_t cmd) +{ + struct sockaddr_un *sa; + unsigned int sa_len; + int sock; + uint16_t nc; + + if ((sock=socket(PF_UNIX,SOCK_STREAM,0))==-1) { + perror("Error: could not open socket"); + exit(2); + } + + sa_len = (offsetof(struct sockaddr_un, sun_path) + strlitlen("/pdnsd.status") + strlen(cache_dir)); + sa=(struct sockaddr_un *)alloca(sa_len+1); + sa->sun_family=AF_UNIX; + stpcpy(stpcpy(sa->sun_path,cache_dir),"/pdnsd.status"); + + if (connect(sock,(struct sockaddr *)sa,sa_len)==-1) { + fprintf(stderr,"Error: could not open socket %s: %s\n",sa->sun_path,strerror(errno)); + close(sock); + exit(2); + } + if(verbose) printf("Opening socket %s\n",sa->sun_path); + + /* Send command code */ + + nc=htons(cmd|CTL_CMDVERNR); /* Add magic number, convert to network byte order. */ + + if (write(sock,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write command code"); + close(sock); + exit(2); + } + + return sock; +} + +static void send_long(int fd,uint32_t cmd) +{ + uint32_t nc=htonl(cmd); + + if (write(fd,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write long"); + close(fd); + exit(2); + } +} + +static void send_short(int fd,uint16_t cmd) +{ + uint16_t nc=htons(cmd); + + if (write(fd,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write short"); + close(fd); + exit(2); + } +} + +#define MAXSENDSTRLEN 0xfffe + +static void send_string(int fd, const char *s) +{ + if(s) { + size_t len=strlen(s); + if(len>MAXSENDSTRLEN) { + fprintf(stderr,"Error: send_string: string length (%lu) exceeds maximum (%u).\n", + (unsigned long)len, MAXSENDSTRLEN); + close(fd); + exit(2); + } + send_short(fd,len); + if (write_all(fd,s,len)!=len) { + perror("Error: could not write string"); + close(fd); + exit(2); + } + } + else + send_short(fd, ~0); +} + +static uint16_t read_short(int fd) +{ + ssize_t err; + uint16_t nc; + + if ((err=read(fd,&nc,sizeof(nc)))!=sizeof(nc)) { + fprintf(stderr,"Error: could not read short: %s\n",err<0?strerror(errno):"unexpected EOF"); + close(fd); + exit(2); + } + return ntohs(nc); +} + +/* copy data from file descriptor fd to file stream out until EOF + or error is encountered. +*/ +static ssize_t copymsgtofile(int fd, FILE* out) +{ + ssize_t n,ntot=0; + char buf[1024]; + + while ((n=read(fd,buf,sizeof(buf)))>0) + ntot+=fwrite(buf,1,n,out); + + if(n<0) + return n; + + return ntot; +} + +static int match_cmd(const char *cmd, const cmd_s cmds[]) +{ + int i; + for(i=0; cmds[i].name; ++i) { + if (strcasecmp(cmd,cmds[i].name)==0) + return cmds[i].val; + } + return -1; +} + +int main(int argc, char *argv[]) +{ + char *cache_dir= NULL; + int rv=0; + { + int i; + char *arg; + for(i=1; i4) + goto wrong_args; + acnt=2; + server_cmd=match_cmd(argv[2],server_cmds); + if(server_cmd==-1) goto bad_arg; + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + send_short(pf,server_cmd); + send_string(pf,argc<4?NULL:argv[3]); + } + goto read_retval; + + case CTL_RECORD: { + int record_cmd; + if (argc!=3) + goto wrong_args; + acnt=2; + record_cmd=match_cmd(argv[2],record_cmds); + if(record_cmd==-1) goto bad_arg; + pf=open_sock(cache_dir, cmd); + send_short(pf,record_cmd); + send_string(pf,argv[1]); + } + goto read_retval; + + case CTL_SOURCE: { + long ttl; + int servaliases,flags; + if (argc<3 || argc>6) + goto wrong_args; + ttl=900; + flags=DF_LOCAL; + acnt=3; + if (argc==6 || (argc>=4 && isdigit(argv[3][0]))) { + char *endptr; + ttl=strtol(argv[3],&endptr,0); + if (*endptr) + goto bad_arg; + acnt++; + } + servaliases=0; + if (acntacnt+2) + goto wrong_args; + + ttl=900; + flags=DF_LOCAL; + pref=0; + if(tp==T_MX) { + char *endptr; + pref=strtol(argv[4],&endptr,0); + if (*endptr) { + acnt=4; + goto bad_arg; + } + } + + if (acnt4) + goto wrong_args; + tp=255; + ttl=900; + acnt=2; + if (argc==3) { + if (isdigit(argv[2][0])) { + char *endptr; + ttl=strtol(argv[2],&endptr,0); + if (*endptr) + goto bad_arg; + } else if ((tp=rr_tp_byname(argv[2]))==-1) { + goto bad_type; + } + } else if (argc==4) { + char *endptr; + if ((tp=rr_tp_byname(argv[2]))==-1) + goto bad_type; + ttl=strtol(argv[3],&endptr,0); + if (*endptr) { + acnt=3; + goto bad_arg; + } + } + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + send_short(pf,tp); + send_long(pf,ttl); + } + goto read_retval; + + case CTL_CONFIG: + if (argc>2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argc<2?NULL:argv[1]); + goto read_retval; + + case CTL_INCLUDE: + if (argc!=2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + goto read_retval; + + case CTL_EVAL: { + int i; size_t bufsz; + + if (argc<2) + goto wrong_args; + bufsz=0; + for(i=1; iMAXSENDSTRLEN) { + fprintf(stderr,"Cannot send 'eval' command: " + "string length (%lu) exceeds maximum (%u).\n", + (unsigned long)bufsz, MAXSENDSTRLEN); + exit(2); + } + pf=open_sock(cache_dir, cmd); + send_short(pf,bufsz); + { + /* Variable-size array for storing the joined strings. */ + char buf[bufsz]; + char *p=buf; + for(i=1; iMAXSENDSTRLEN) { + fprintf(stderr,"Cannot send 'empty' command: " + "string length (%lu) exceeds maximum (%u).\n", + (unsigned long)totsz, MAXSENDSTRLEN); + exit(2); + } + pf=open_sock(cache_dir, cmd); + if(argc>1) { + send_short(pf,totsz); + for(i=1; i2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argc<2?NULL:argv[1]); + copy_pf: + if((rv=read_short(pf))) + goto retval_failed; + if(copymsgtofile(pf,stdout)<0) { + perror("Error while reading from socket"); + close(pf); + exit(2); + } + goto close_pf; + + read_retval: + if((rv=read_short(pf))) { + retval_failed: + fprintf(stderr,"Failed: "); + if(copymsgtofile(pf,stderr)<0) + fprintf(stderr,"(could not read error message from socket: %s)",strerror(errno)); + + fputc('\n',stderr); + } + close_pf: + if(close(pf)==-1) + perror("Couldn't close socket"); + else if (rv==0 && verbose) + printf("Succeeded\n"); + break; + wrong_args: + fprintf(stderr,"Wrong number of arguments for '%s' command.\n",argv[0]); + goto print_cmd_usage; + bad_arg: + fprintf(stderr,"Bad argument for '%s' command: %s\n",argv[0],argv[acnt]); + print_cmd_usage: + fprintf(stderr,"Usage:\n\n%s\n" + "Try 'pdnsd-ctl help' for a description of all available commands and options.\n", + help_messages[cmd]); + exit(2); + bad_type: + fprintf(stderr,"Bad (type) argument for '%s' command: %s\n" + "Run 'pdnsd-ctl list-rrtypes' for a list of available rr types.\n", + argv[0],argv[acnt]); + exit(2); + } + } + + return rv; +} + diff --git a/app/src/main/jni/pdnsd/src/pdnsd_assert.h b/app/src/main/jni/pdnsd/src/pdnsd_assert.h new file mode 100644 index 0000000..0acdfc2 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/pdnsd_assert.h @@ -0,0 +1,51 @@ +/* This include file was added by Paul A. Rombouts. + I had terrible difficulties with cyclic dependencies of the include files + written by Thomas Moestl. The only way I knew how to break the cycle was to + put some declarations in a seperate file. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef PDNSD_ASSERT_H +#define PDNSD_ASSERT_H + +/* Originally in helpers.h */ + +/* format string checking for printf-like functions */ +#ifdef __GNUC__ +#define printfunc(fmt, firstva) __attribute__((__format__(__printf__, fmt, firstva))) +#else +#define printfunc(fmt, firstva) +#endif + +void pdnsd_exit(void); + + +/* + * Assert macro, used in some places. For now, it should be always defined, not + * only in the DEBUG case, to be on the safe side security-wise. + */ +#define PDNSD_ASSERT(cond, msg) \ + { if (!(cond)) { \ + log_error("%s:%d: %s", __FILE__, __LINE__, msg); \ + pdnsd_exit(); \ + } } + +#endif diff --git a/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am b/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am new file mode 100644 index 0000000..2a7b420 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am @@ -0,0 +1,7 @@ + +install-exec-local: + if [ "$(distribution)" = "ArchLinux" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/pdnsd";\ + fi + diff --git a/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in b/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in new file mode 100644 index 0000000..6af6f9e --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in @@ -0,0 +1,332 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/ArchLinux +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/ArchLinux/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/ArchLinux/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "ArchLinux" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/pdnsd";\ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in b/app/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in new file mode 100644 index 0000000..c392750 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in @@ -0,0 +1,45 @@ +#!/bin/bash + +. /etc/rc.conf +. /etc/rc.d/functions + +PID=`pidof -o %PPID @prefix@/sbin/pdnsd` + +start() { + stat_busy "Starting PDNSD" + [ -z "$PID" ] && @prefix@/sbin/pdnsd -d -c /etc/pdnsd.conf + if [ $? -gt 0 ]; then + stat_fail + else + add_daemon pdnsd + stat_done + fi +} + +stop() { + stat_busy "Stopping PDNSD" + [ ! -z "$PID" ] && kill $PID &> /dev/null + if [ $? -gt 0 ]; then + stat_fail + else + rm_daemon pdnsd + stat_done + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + $0 stop + sleep 2 + $0 start + ;; + *) + echo "usage: $0 {start|stop|restart}" +esac +exit 0 diff --git a/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.am b/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.am new file mode 100644 index 0000000..61d3eb3 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.am @@ -0,0 +1,8 @@ + +install-exec-local: + if [ "$(distribution)" = "Debian" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/etc/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/init.d"; \ + update-rc.d pdnsd defaults 19 ;\ + fi diff --git a/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.in b/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.in new file mode 100644 index 0000000..992a059 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Debian/Makefile.in @@ -0,0 +1,334 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/Debian +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Debian/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Debian/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "Debian" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/etc/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/init.d"; \ + update-rc.d pdnsd defaults 19 ;\ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in b/app/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in new file mode 100644 index 0000000..068606e --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in @@ -0,0 +1,52 @@ +#!/bin/sh + +# +# This script was written and contributed by Markus Mohr, and +# slightly modified by me for version 1.0.6 (which obviously +# broke it some way). I then applied a set of corrections +# by Markus Mohr. +# +# Carsten Block has patched this with some magic so that +# the actual script is generated by configure, and that +# the pdnsd user is determined from pdnsd.conf +# I changed this a little to use the --pdnsd-user option +# of pdnsd to determine the run_as user. +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +test -x @prefix@/sbin/pdnsd || exit 0 + +case "$1" in + start) + # Check if cache dir exists and recreate if neccessary + test -d @cachedir@ || mkdir @cachedir@ + RUNAS=`@prefix@/sbin/pdnsd --pdnsd-user` || echo -n " failed" + [ -z "$RUNAS" ] && RUNAS=nobody + chown $RUNAS @cachedir@ + echo -n "Starting domain name service: pdnsd" + start-stop-daemon --start --quiet --pidfile /var/run/pdnsd.pid --name pdnsd \ + --exec @prefix@/sbin/pdnsd -- --daemon -p /var/run/pdnsd.pid \ + || echo -n " failed" + echo "." + ;; + + stop) + echo -n "Stopping domain name service: pdnsd" + start-stop-daemon --stop --quiet --pidfile /var/run/pdnsd.pid --name pdnsd --exec @prefix@/sbin/pdnsd \ + || echo -n " failed" + echo "." + ;; + + restart) + $0 stop + $0 start + ;; + + *) + echo "Usage: /etc/init.d/pdnsd {start|stop|restart}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/app/src/main/jni/pdnsd/src/rc/Makefile.am b/app/src/main/jni/pdnsd/src/rc/Makefile.am new file mode 100644 index 0000000..abf2e6d --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Makefile.am @@ -0,0 +1,5 @@ + +SUBDIRS = RedHat SuSE Debian Slackware ArchLinux + +EXTRA_DIST = README + diff --git a/app/src/main/jni/pdnsd/src/rc/Makefile.in b/app/src/main/jni/pdnsd/src/rc/Makefile.in new file mode 100644 index 0000000..38ee297 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Makefile.in @@ -0,0 +1,526 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = RedHat SuSE Debian Slackware ArchLinux +EXTRA_DIST = README +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic ctags \ + ctags-recursive distclean distclean-generic distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ + tags-recursive uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/README b/app/src/main/jni/pdnsd/src/rc/README new file mode 100644 index 0000000..4e00764 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/README @@ -0,0 +1,104 @@ +rc/ +=== + +These are start scripts for different Linux distros and other things that +do not directly belong to pdnsd. +If you do start scripts for the distro you use, please GPL them and send +them in, so that they can be included in this package for other users. +Note that there is NO WARRANTY OF ANY KIND on anything in this directory; +read the COPYING that comes with pdnsd for details. +So far there are files in the following directories: + +SuSE +---- +pdnsd - Start script for SuSE Linux. Tested for 6.? but should run on some + versions below. You can do 'make install' as root in the SuSE + directory to install it, or you can install manually: + --manual installation------------------------------------------------- + For manual installation, copy it into /sbin/init.d/, go to + /sbin/init.d/rc2.d/ and create there the following two symlinks: + S11pdnsd -> ../pdnsd (do "ln -s ../pdnsd S11pdnsd" in that dir) + K34pdnsd -> ../pdnsd (do "ln -s ../pdnsd K34pdnsd" in that dir) + The numbers dictate the order different services are started and + might need to be modified. Then edit your /etc/rc.config file and + add the line "START_PDNSD=yes" to start pdnsd at boot time. + ---------------------------------------------------------------------- + If you used the 'make install' command, "START_PDNSD=yes" has been + appended to your /etc/rc.config file, causing pdnsd to be started + at boot time. If you don't want that, change the "yes" into "no". + This start script was created from /sbin/init.d/skeleton by me, so the + most is copyrighted by SuSE. They put it under the GPL, however, so + the licence stated in COPYING also applies to this script. + This is no official SuSE script, and SuSE naturally does NO support + for it. + +Redhat +------ +The contents of the Redhat directory and the following documentation were +contributed by Torben Janssen. Thanks a lot! + +pdnsd - Start script for Redhat Linux. Tested for 6.1 but should run on 5.0+. + You can do 'make install' as root in the Redhat directory to + install it, or you can install manually: + + --manual installation------------------------------------------------- + For manual installation, copy pdnsd into /etc/rc.d/init.d/ + + Then go to /etc/rc.d/rc3.d and create there the following symlink: + S78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd S78pdnsd" in that dir) + + Then go to /etc/rc.d/rc0.d and create there the following symlink: + K78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd K78pdnsd" in that dir) + + Then go to /etc/rc.d/rc6.d and create there the following symlink: + K78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd K78pdnsd" in that dir) + + WHY + --- + the rc[0-6].d dirs includes the scripts which starts/stops the + services on entering runlevel [0-6] + the interesting runlevels on Redhat are: + 0 - halt + 3 - multi user system + 6 - reboot + The links have an 'S' or 'K' and a number at the beginnig. All links + with 'S' starts the script on entering the runlevel and 'K' stops + them. + So, there's an 'S' link in rc3 and 'K' links in rc0 and rc6. + I choose 78 as number, because there was no script with this number on + my system. You can choose every number you want, AFAIK . + + This is no offical Redhat script, and Redhat naturally does NO support + for it. + +Debian +------ +The contents of the Debian directory were contributed by Markus Mohr. +His installation instructions are (translated): +Install the pdnsd script to /etc/init and run +update-rc.d pdnsd defaults 19 +as root. + +Slackware +--------- +A Slackware start-up script rc.pdnsd was contributed by Nikola Kotur . +His comments were: + +Slackware uses traditional BSD style init script layout instead of SystemV +style startup scripts. So I adjusted the start-up script for pdnsd, and +now it can be used with Slackware 9.1 distribution, and probably with all the +others. + +Additional info: +1) put these lines in the /etc/rc.d/rc.M: + if [ -x /etc/rc.d/rc.pdnsd ]; then + /etc/rc.d/rc.pdnsd start + fi + +2) put these lines in the /etc/rc.d/rc.6 and /etc/rc.d/rc.K: + if [ -x /etc/rc.d/rc.pdnsd ]; then + /etc/rc.d/rc.pdnsd stop + fi diff --git a/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am b/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am new file mode 100644 index 0000000..cb8de88 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am @@ -0,0 +1,12 @@ + +# no need to create links. 'chkconfig' will take care of this. +# In the spec case, chkconfig is called during rpm install +install-exec-local: + if [ "$(distribution)" = "RedHat" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/init.d/pdnsd"; \ + if [ "$(specbuild)" = "no" ] ; then \ + /sbin/chkconfig --add pdnsd; \ + fi \ + fi + diff --git a/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in b/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in new file mode 100644 index 0000000..a88a037 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in @@ -0,0 +1,337 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/RedHat +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/RedHat/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/RedHat/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +# no need to create links. 'chkconfig' will take care of this. +# In the spec case, chkconfig is called during rpm install +install-exec-local: + if [ "$(distribution)" = "RedHat" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/init.d/pdnsd"; \ + if [ "$(specbuild)" = "no" ] ; then \ + /sbin/chkconfig --add pdnsd; \ + fi \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in b/app/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in new file mode 100644 index 0000000..b6d9081 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in @@ -0,0 +1,88 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/pdnsd +# +# Script for starting the Proxy DNS Daemon +# Modified by Paul Rombouts, 2003 +# +# chkconfig: 2345 11 89 +# description: Proxy DNS Daemon +# processname: pdnsd +# config: /etc/pdnsd.conf + +PATH=/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin + +# Source function library. +. /etc/rc.d/init.d/functions + +# Source networking configuration. +. /etc/sysconfig/network + +# Check that networking is up. +if [[ $NETWORKING == [Nn][Oo] ]]; then exit 0; fi + +# Source sysconfig settings, if any. +if [ -f /etc/sysconfig/pdnsd ]; then . /etc/sysconfig/pdnsd; fi + +start() { + echo -n 'Starting pdnsd: ' + daemon @prefix@/sbin/pdnsd -d -s -p /var/run/pdnsd.pid "$EXTRAOPTIONS" + local RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then touch /var/lock/subsys/pdnsd; fi + return $RETVAL +} + +stop() { + echo -n 'Shutting down pdnsd: ' + killproc pdnsd + local RETVAL=$? + case @threadlib@ in + [Ll]inux[Tt]hreads*|lt*) + # Wait until all threads have terminated. + local -i count=20 + while [[ count -gt 0 ]] && pidof pdnsd > /dev/null + do + usleep 200000 + let --count + done + ;; + esac + echo + if [ $RETVAL -eq 0 ]; then rm -f /var/lock/subsys/pdnsd; fi + return $RETVAL +} + +restart() { + stop + start +} + +# +# See how we were called. +# +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status pdnsd + ;; + reload) + @prefix@/sbin/pdnsd-ctl config + ;; + restart) + restart + ;; + condrestart) + if [ -f /var/lock/subsys/pdnsd ]; then restart; fi + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}" + exit 1 +esac + +exit diff --git a/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am b/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am new file mode 100644 index 0000000..e44b50f --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am @@ -0,0 +1,3 @@ +# TODO: write an install rule for the Slackware start-up script. + +install-exec-local: diff --git a/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in b/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in new file mode 100644 index 0000000..14ebd33 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in @@ -0,0 +1,330 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# TODO: write an install rule for the Slackware start-up script. +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/Slackware +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/rc.pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = rc.pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Slackware/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Slackware/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +rc.pdnsd: $(top_builddir)/config.status $(srcdir)/rc.pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in b/app/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in new file mode 100644 index 0000000..6e96971 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in @@ -0,0 +1,74 @@ +#!/bin/bash +# +# /etc/rc.d/rc.pdnsd +# +# Starts the Proxy DNS Daemon +# +# description: Proxy DNS Daemon +# processname: pdnsd +# config: /etc/pdnsd.conf +# distribution: Slackware +# author: Nikola Kotur +# +# Additional info: +# 1) put these lines in the /etc/rc.d/rc.M: +# if [ -x /etc/rc.d/rc.pdnsd ]; then +# /etc/rc.d/rc.pdnsd start +# fi +# +# 2) put these lines in the /etc/rc.d/rc.6 and /etc/rc.d/rc.K: +# if [ -x /etc/rc.d/rc.pdnsd ]; then +# /etc/rc.d/rc.pdnsd stop +# fi + + +test -x @prefix@/sbin/pdnsd || exit 0 +[ -f @sysconfdir@/pdnsd.conf ] || exit 1 + +RETVAL=0 + +start() { + echo -n "Starting pdnsd... " + RETVAL=$? + @prefix@/sbin/pdnsd -d + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/pdnsd + echo ' OK' +} + +stop() { + echo -n "Shutting down pdnsd... " + killall pdnsd + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/pdnsd + echo ' OK' +} + +restart() { + stop + start +} + +condrestart() { + [ -e /var/lock/subsys/pdnsd ] && restart + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + reload|restart) + restart + ;; + condrestart) + condrestart + ;; + *) + echo $"Usage: $0 {start|stop|restart|condrestart|reload}" + RETVAL=1 +esac + +exit $RETVAL diff --git a/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am b/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am new file mode 100644 index 0000000..dc5f485 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am @@ -0,0 +1,22 @@ + +install-exec-local: + if [ "$(distribution)" = "SuSE" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/sbin/init.d/pdnsd"; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc2.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc2.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc3.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc3.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + grep "START_PDNSD" "$(DESTDIR)/etc/rc.config" > /dev/null ; \ + if [ $$? -eq 1 ] ; then \ + echo -e "\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" >> /etc/rc.config ; \ + fi \ + fi + + + diff --git a/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in b/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in new file mode 100644 index 0000000..df1660f --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in @@ -0,0 +1,345 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/SuSE +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/SuSE/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/SuSE/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "SuSE" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/sbin/init.d/pdnsd"; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc2.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc2.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc3.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc3.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + grep "START_PDNSD" "$(DESTDIR)/etc/rc.config" > /dev/null ; \ + if [ $$? -eq 1 ] ; then \ + echo -e "\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" >> /etc/rc.config ; \ + fi \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in b/app/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in new file mode 100644 index 0000000..9711eca --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in @@ -0,0 +1,68 @@ +#! /bin/sh +# Copyright (c) 1995-1998 SuSE GmbH Nuernberg, Germany. +# +# Modified 2000 from SuSE Linux 6.3 /sbin/init.d/skeleton by Thomas Moestl +# +# /sbin/init.d/pdnsd +# +# and symbolic its link +# +# /sbin/rc?/pdnsd +# + +. /etc/rc.config + +# Determine the base and follow a runlevel link name. +base=${0##*/} +link=${base#*[SK][0-9][0-9]} + +# Force execution if not called by a runlevel directory. +test $link = $base && START_PDNSD=yes +test "$START_PDNSD" = yes || exit 0 + +# The echo return value for success (defined in /etc/rc.config). +return=$rc_done +case "$1" in + start) + echo -n "Starting pdnsd" + ## Start daemon with startproc(8). If this fails + ## the echo return value is set appropriate. + + startproc @prefix@/sbin/pdnsd -d || return=$rc_failed + + echo -e "$return" + ;; + stop) + echo -n "Shutting down pdnsd" + ## Stop daemon with killproc(8) and if this fails + ## set echo the echo return value. + + killproc -TERM @prefix@/sbin/pdnsd || return=$rc_failed + + echo -e "$return" + ;; + restart) + ## If first returns OK call the second, if first or + ## second command fails, set echo return value. + $0 stop && $0 start || return=$rc_failed + ;; + reload) + $0 stop && $0 start || return=$rc_failed + ;; + status) + echo -n "Checking for pdnsd: " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + checkproc @prefix@/sbin/pdnsd && echo OK || echo No process + ;; + *) + echo "Usage: $0 {start|stop|status|restart|reload}" + exit 1 + ;; +esac + +# Inform the caller not only verbosely and set an exit status. +test "$return" = "$rc_done" || exit 1 +exit 0 + diff --git a/app/src/main/jni/pdnsd/src/rr_types.c b/app/src/main/jni/pdnsd/src/rr_types.c new file mode 100644 index 0000000..275a90a --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rr_types.c @@ -0,0 +1,172 @@ +/* rr_types.c - Tables with information for handling + all rr types known to pdnsd, plus + some helper functions useful for turning + binary RR data into text or vice versa. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#define DEFINE_RR_TYPE_ARRAYS 1 +#include "helpers.h" +#include "dns.h" +#include "rr_types.h" + + +/* + * OK, this is inefficient. But it is used _really_ seldom (only in some cases while parsing the + * config file or by pdnsd-ctl), so it is much more effective to sort by id. + */ +int rr_tp_byname(char *name) +{ + int i; + + for (i=0;i> 4); + exponent = (prec & 0x0f); + + if(mantissa>=10 || exponent>=10) + return NULL; + if (exponent>= 2) + sprintf(retbuf, "%u", mantissa * poweroften[exponent-2]); + else + sprintf(retbuf, "0.%.2u", mantissa * poweroften[exponent]); + return (retbuf); +} + +/* takes an on-the-wire LOC RR and formats it in a human readable format. + This is an adapted version of the loc_ntoa function that + can be found in the BIND 9 source. + */ +const char *loc2str(const void *binary, char *ascii, size_t asclen) +{ + const unsigned char *cp = binary; + + int latdeg, latmin, latsec, latsecfrac; + int longdeg, longmin, longsec, longsecfrac; + char northsouth, eastwest; + const char *altsign; + int altmeters, altfrac; + + const uint32_t referencealt = 100000 * 100; + + int32_t latval, longval, altval; + uint32_t templ; + uint8_t sizeval, hpval, vpval, versionval; + + char sizestr[NPRECSIZE],hpstr[NPRECSIZE],vpstr[NPRECSIZE]; + + versionval = *cp++; + + if (versionval) { + /* unknown LOC RR version */ + return NULL; + } + + sizeval = *cp++; + + hpval = *cp++; + vpval = *cp++; + + GETINT32(templ, cp); + latval = (templ - ((unsigned)1<<31)); + + GETINT32(templ, cp); + longval = (templ - ((unsigned)1<<31)); + + GETINT32(templ, cp); + if (templ < referencealt) { /* below WGS 84 spheroid */ + altval = referencealt - templ; + altsign = "-"; + } else { + altval = templ - referencealt; + altsign = ""; + } + + if (latval < 0) { + northsouth = 'S'; + latval = -latval; + } else + northsouth = 'N'; + + latsecfrac = latval % 1000; + latval /= 1000; + latsec = latval % 60; + latval /= 60; + latmin = latval % 60; + latval /= 60; + latdeg = latval; + + if (longval < 0) { + eastwest = 'W'; + longval = -longval; + } else + eastwest = 'E'; + + longsecfrac = longval % 1000; + longval /= 1000; + longsec = longval % 60; + longval /= 60; + longmin = longval % 60; + longval /= 60; + longdeg = longval; + + altfrac = altval % 100; + altmeters = (altval / 100); + + if(!precsize_ntoa(sizeval,sizestr) || !precsize_ntoa(hpval,hpstr) || !precsize_ntoa(vpval,vpstr)) + return NULL; + { + int n=snprintf(ascii,asclen, + "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %s%d.%.2dm %sm %sm %sm", + latdeg, latmin, latsec, latsecfrac, northsouth, + longdeg, longmin, longsec, longsecfrac, eastwest, + altsign, altmeters, altfrac, + sizestr, hpstr, vpstr); + if(n<0 || n>=asclen) + return NULL; + } + + return (ascii); +} + +#endif diff --git a/app/src/main/jni/pdnsd/src/rr_types.h b/app/src/main/jni/pdnsd/src/rr_types.h new file mode 100644 index 0000000..6025fae --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rr_types.h @@ -0,0 +1,536 @@ +/* This file was generated by running 'make_rr_types_h.pl rr_types.in'. + Modifications to this file may be lost the next time it is automatically + regenerated. +*/ + +/* rr_types.h - A header file with names & descriptions of + all rr types known to pdnsd + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef _RR_TYPES_H_ +#define _RR_TYPES_H_ + +#include + +#define T_MIN 1 +#define T_A 1 +#define T_NS 2 +#define T_MD 3 +#define T_MF 4 +#define T_CNAME 5 +#define T_SOA 6 +#define T_MB 7 +#define T_MG 8 +#define T_MR 9 +#define T_NULL 10 +#define T_WKS 11 +#define T_PTR 12 +#define T_HINFO 13 +#define T_MINFO 14 +#define T_MX 15 +#define T_TXT 16 +#define T_RP 17 +#define T_AFSDB 18 +#define T_X25 19 +#define T_ISDN 20 +#define T_RT 21 +#define T_NSAP 22 +#define T_NSAP_PTR 23 +#define T_SIG 24 +#define T_KEY 25 +#define T_PX 26 +#define T_GPOS 27 +#define T_AAAA 28 +#define T_LOC 29 +#define T_NXT 30 +#define T_EID 31 +#define T_NIMLOC 32 +#define T_SRV 33 +#define T_ATMA 34 +#define T_NAPTR 35 +#define T_KX 36 +#define T_CERT 37 +#define T_A6 38 +#define T_DNAME 39 +#define T_SINK 40 +#define T_OPT 41 +#define T_APL 42 +#define T_DS 43 +#define T_SSHFP 44 +#define T_IPSECKEY 45 +#define T_RRSIG 46 +#define T_NSEC 47 +#define T_DNSKEY 48 +#define T_DHCID 49 +#define T_NSEC3 50 +#define T_NSEC3PARAM 51 +#define T_HIP 55 +#define T_NINFO 56 +#define T_RKEY 57 +#define T_TALINK 58 +#define T_SPF 99 +#define T_UINFO 100 +#define T_UID 101 +#define T_GID 102 +#define T_UNSPEC 103 +#define T_MAX 51 + +/* T_MAX - T_MIN + 1 */ +#define T_NUM 51 + +/* Number of most frequently used rr types. */ +#define NRRMU 8 + +/* Number of remaining rr types. */ +#define NRREXT 39 + +/* NRRMU + NRREXT */ +#define NRRTOT 47 + +/* Lookup table for converting rr type values to internally used indices. */ +extern const unsigned short int rrlkuptab[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrlkuptab[T_NUM] = { + 0 /* A */, + 1 /* NS */, + 8 /* MD */, + 9 /* MF */, + 2 /* CNAME */, + 3 /* SOA */, + 10 /* MB */, + 11 /* MG */, + 12 /* MR */, + 13 /* NULL */, + 14 /* WKS */, + 4 /* PTR */, + 15 /* HINFO */, + 16 /* MINFO */, + 5 /* MX */, + 6 /* TXT */, + 17 /* RP */, + 18 /* AFSDB */, + 19 /* X25 */, + 20 /* ISDN */, + 21 /* RT */, + 22 /* NSAP */, + 23 /* NSAP_PTR */, + 24 /* SIG */, + 25 /* KEY */, + 26 /* PX */, + 27 /* GPOS */, + 7 /* AAAA */, + 28 /* LOC */, + 29 /* NXT */, + 30 /* EID */, + 31 /* NIMLOC */, + 32 /* SRV */, + 33 /* ATMA */, + 34 /* NAPTR */, + 35 /* KX */, + 36 /* CERT */, + 47 /* A6 */, + 48 /* DNAME */, + 49 /* SINK */, + 50 /* OPT */, + 37 /* APL */, + 38 /* DS */, + 39 /* SSHFP */, + 40 /* IPSECKEY */, + 41 /* RRSIG */, + 42 /* NSEC */, + 43 /* DNSKEY */, + 44 /* DHCID */, + 45 /* NSEC3 */, + 46 /* NSEC3PARAM */ +}; +#endif + +/* List of most frequently used RR types in ascending order. */ +extern const unsigned short int rrmuiterlist[NRRMU]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrmuiterlist[NRRMU] = { + T_A, + T_NS, + T_CNAME, + T_SOA, + T_PTR, + T_MX, + T_TXT, + T_AAAA +}; +#endif + +/* List of the cache-able RR types in ascending order. */ +extern const unsigned short int rrcachiterlist[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS +const unsigned short int rrcachiterlist[NRRTOT] = { + T_A, + T_NS, + T_MD, + T_MF, + T_CNAME, + T_SOA, + T_MB, + T_MG, + T_MR, + T_NULL, + T_WKS, + T_PTR, + T_HINFO, + T_MINFO, + T_MX, + T_TXT, + T_RP, + T_AFSDB, + T_X25, + T_ISDN, + T_RT, + T_NSAP, + T_NSAP_PTR, + T_SIG, + T_KEY, + T_PX, + T_GPOS, + T_AAAA, + T_LOC, + T_NXT, + T_EID, + T_NIMLOC, + T_SRV, + T_ATMA, + T_NAPTR, + T_KX, + T_CERT, + T_APL, + T_DS, + T_SSHFP, + T_IPSECKEY, + T_RRSIG, + T_NSEC, + T_DNSKEY, + T_DHCID, + T_NSEC3, + T_NSEC3PARAM +}; +#endif + +/* Optimized getrrset macros for fixed rr types. */ +#define getrrset_A(cent) GET_RRSMU(cent,0) +#define getrrset_NS(cent) GET_RRSMU(cent,1) +#define getrrset_MD(cent) GET_RRSEXT(cent,0) +#define getrrset_MF(cent) GET_RRSEXT(cent,1) +#define getrrset_CNAME(cent) GET_RRSMU(cent,2) +#define getrrset_SOA(cent) GET_RRSMU(cent,3) +#define getrrset_MB(cent) GET_RRSEXT(cent,2) +#define getrrset_MG(cent) GET_RRSEXT(cent,3) +#define getrrset_MR(cent) GET_RRSEXT(cent,4) +#define getrrset_NULL(cent) GET_RRSEXT(cent,5) +#define getrrset_WKS(cent) GET_RRSEXT(cent,6) +#define getrrset_PTR(cent) GET_RRSMU(cent,4) +#define getrrset_HINFO(cent) GET_RRSEXT(cent,7) +#define getrrset_MINFO(cent) GET_RRSEXT(cent,8) +#define getrrset_MX(cent) GET_RRSMU(cent,5) +#define getrrset_TXT(cent) GET_RRSMU(cent,6) +#define getrrset_RP(cent) GET_RRSEXT(cent,9) +#define getrrset_AFSDB(cent) GET_RRSEXT(cent,10) +#define getrrset_X25(cent) GET_RRSEXT(cent,11) +#define getrrset_ISDN(cent) GET_RRSEXT(cent,12) +#define getrrset_RT(cent) GET_RRSEXT(cent,13) +#define getrrset_NSAP(cent) GET_RRSEXT(cent,14) +#define getrrset_NSAP_PTR(cent) GET_RRSEXT(cent,15) +#define getrrset_SIG(cent) GET_RRSEXT(cent,16) +#define getrrset_KEY(cent) GET_RRSEXT(cent,17) +#define getrrset_PX(cent) GET_RRSEXT(cent,18) +#define getrrset_GPOS(cent) GET_RRSEXT(cent,19) +#define getrrset_AAAA(cent) GET_RRSMU(cent,7) +#define getrrset_LOC(cent) GET_RRSEXT(cent,20) +#define getrrset_NXT(cent) GET_RRSEXT(cent,21) +#define getrrset_EID(cent) GET_RRSEXT(cent,22) +#define getrrset_NIMLOC(cent) GET_RRSEXT(cent,23) +#define getrrset_SRV(cent) GET_RRSEXT(cent,24) +#define getrrset_ATMA(cent) GET_RRSEXT(cent,25) +#define getrrset_NAPTR(cent) GET_RRSEXT(cent,26) +#define getrrset_KX(cent) GET_RRSEXT(cent,27) +#define getrrset_CERT(cent) GET_RRSEXT(cent,28) +#define getrrset_APL(cent) GET_RRSEXT(cent,29) +#define getrrset_DS(cent) GET_RRSEXT(cent,30) +#define getrrset_SSHFP(cent) GET_RRSEXT(cent,31) +#define getrrset_IPSECKEY(cent) GET_RRSEXT(cent,32) +#define getrrset_RRSIG(cent) GET_RRSEXT(cent,33) +#define getrrset_NSEC(cent) GET_RRSEXT(cent,34) +#define getrrset_DNSKEY(cent) GET_RRSEXT(cent,35) +#define getrrset_DHCID(cent) GET_RRSEXT(cent,36) +#define getrrset_NSEC3(cent) GET_RRSEXT(cent,37) +#define getrrset_NSEC3PARAM(cent) GET_RRSEXT(cent,38) + +/* have_rr macros for fixed rr types. */ +#define have_rr_A(cent) HAVE_RRMU(cent,0) +#define have_rr_NS(cent) HAVE_RRMU(cent,1) +#define have_rr_MD(cent) HAVE_RREXT(cent,0) +#define have_rr_MF(cent) HAVE_RREXT(cent,1) +#define have_rr_CNAME(cent) HAVE_RRMU(cent,2) +#define have_rr_SOA(cent) HAVE_RRMU(cent,3) +#define have_rr_MB(cent) HAVE_RREXT(cent,2) +#define have_rr_MG(cent) HAVE_RREXT(cent,3) +#define have_rr_MR(cent) HAVE_RREXT(cent,4) +#define have_rr_NULL(cent) HAVE_RREXT(cent,5) +#define have_rr_WKS(cent) HAVE_RREXT(cent,6) +#define have_rr_PTR(cent) HAVE_RRMU(cent,4) +#define have_rr_HINFO(cent) HAVE_RREXT(cent,7) +#define have_rr_MINFO(cent) HAVE_RREXT(cent,8) +#define have_rr_MX(cent) HAVE_RRMU(cent,5) +#define have_rr_TXT(cent) HAVE_RRMU(cent,6) +#define have_rr_RP(cent) HAVE_RREXT(cent,9) +#define have_rr_AFSDB(cent) HAVE_RREXT(cent,10) +#define have_rr_X25(cent) HAVE_RREXT(cent,11) +#define have_rr_ISDN(cent) HAVE_RREXT(cent,12) +#define have_rr_RT(cent) HAVE_RREXT(cent,13) +#define have_rr_NSAP(cent) HAVE_RREXT(cent,14) +#define have_rr_NSAP_PTR(cent) HAVE_RREXT(cent,15) +#define have_rr_SIG(cent) HAVE_RREXT(cent,16) +#define have_rr_KEY(cent) HAVE_RREXT(cent,17) +#define have_rr_PX(cent) HAVE_RREXT(cent,18) +#define have_rr_GPOS(cent) HAVE_RREXT(cent,19) +#define have_rr_AAAA(cent) HAVE_RRMU(cent,7) +#define have_rr_LOC(cent) HAVE_RREXT(cent,20) +#define have_rr_NXT(cent) HAVE_RREXT(cent,21) +#define have_rr_EID(cent) HAVE_RREXT(cent,22) +#define have_rr_NIMLOC(cent) HAVE_RREXT(cent,23) +#define have_rr_SRV(cent) HAVE_RREXT(cent,24) +#define have_rr_ATMA(cent) HAVE_RREXT(cent,25) +#define have_rr_NAPTR(cent) HAVE_RREXT(cent,26) +#define have_rr_KX(cent) HAVE_RREXT(cent,27) +#define have_rr_CERT(cent) HAVE_RREXT(cent,28) +#define have_rr_A6(cent) 0 +#define have_rr_DNAME(cent) 0 +#define have_rr_SINK(cent) 0 +#define have_rr_OPT(cent) 0 +#define have_rr_APL(cent) HAVE_RREXT(cent,29) +#define have_rr_DS(cent) HAVE_RREXT(cent,30) +#define have_rr_SSHFP(cent) HAVE_RREXT(cent,31) +#define have_rr_IPSECKEY(cent) HAVE_RREXT(cent,32) +#define have_rr_RRSIG(cent) HAVE_RREXT(cent,33) +#define have_rr_NSEC(cent) HAVE_RREXT(cent,34) +#define have_rr_DNSKEY(cent) HAVE_RREXT(cent,35) +#define have_rr_DHCID(cent) HAVE_RREXT(cent,36) +#define have_rr_NSEC3(cent) HAVE_RREXT(cent,37) +#define have_rr_NSEC3PARAM(cent) HAVE_RREXT(cent,38) + +/* These macros specify which RR types are cached by pdnsd. */ +#define IS_CACHED_A 1 +#define IS_CACHED_NS 1 +#define IS_CACHED_MD 1 +#define IS_CACHED_MF 1 +#define IS_CACHED_CNAME 1 +#define IS_CACHED_SOA 1 +#define IS_CACHED_MB 1 +#define IS_CACHED_MG 1 +#define IS_CACHED_MR 1 +#define IS_CACHED_NULL 1 +#define IS_CACHED_WKS 1 +#define IS_CACHED_PTR 1 +#define IS_CACHED_HINFO 1 +#define IS_CACHED_MINFO 1 +#define IS_CACHED_MX 1 +#define IS_CACHED_TXT 1 +#define IS_CACHED_RP 1 +#define IS_CACHED_AFSDB 1 +#define IS_CACHED_X25 1 +#define IS_CACHED_ISDN 1 +#define IS_CACHED_RT 1 +#define IS_CACHED_NSAP 1 +#define IS_CACHED_NSAP_PTR 1 +#define IS_CACHED_SIG 1 +#define IS_CACHED_KEY 1 +#define IS_CACHED_PX 1 +#define IS_CACHED_GPOS 1 +#define IS_CACHED_AAAA 1 +#define IS_CACHED_LOC 1 +#define IS_CACHED_NXT 1 +#define IS_CACHED_EID 1 +#define IS_CACHED_NIMLOC 1 +#define IS_CACHED_SRV 1 +#define IS_CACHED_ATMA 1 +#define IS_CACHED_NAPTR 1 +#define IS_CACHED_KX 1 +#define IS_CACHED_CERT 1 +#define IS_CACHED_APL 1 +#define IS_CACHED_DS 1 +#define IS_CACHED_SSHFP 1 +#define IS_CACHED_IPSECKEY 1 +#define IS_CACHED_RRSIG 1 +#define IS_CACHED_NSEC 1 +#define IS_CACHED_DNSKEY 1 +#define IS_CACHED_DHCID 1 +#define IS_CACHED_NSEC3 1 +#define IS_CACHED_NSEC3PARAM 1 + +/* Array indices for most frequently used rr types. */ +#define RRMUINDEX_A 0 +#define RRMUINDEX_NS 1 +#define RRMUINDEX_CNAME 2 +#define RRMUINDEX_SOA 3 +#define RRMUINDEX_PTR 4 +#define RRMUINDEX_MX 5 +#define RRMUINDEX_TXT 6 +#define RRMUINDEX_AAAA 7 + +/* Table of rr names. */ +extern const char *const rrnames[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS +const char *const rrnames[T_NUM] = { + "A", + "NS", + "MD", + "MF", + "CNAME", + "SOA", + "MB", + "MG", + "MR", + "NULL", + "WKS", + "PTR", + "HINFO", + "MINFO", + "MX", + "TXT", + "RP", + "AFSDB", + "X25", + "ISDN", + "RT", + "NSAP", + "NSAP_PTR", + "SIG", + "KEY", + "PX", + "GPOS", + "AAAA", + "LOC", + "NXT", + "EID", + "NIMLOC", + "SRV", + "ATMA", + "NAPTR", + "KX", + "CERT", + "A6", + "DNAME", + "SINK", + "OPT", + "APL", + "DS", + "SSHFP", + "IPSECKEY", + "RRSIG", + "NSEC", + "DNSKEY", + "DHCID", + "NSEC3", + "NSEC3PARAM" +}; +#endif + +/* Structure for rr information */ +struct rr_infos { + unsigned short class; /* class (values see below) */ + unsigned short excludes; /* relations to other classes. + Mutual exclusion is marked by or'ing the + respective RRCL value in this field. + Exclusions should be symmetric. */ +}; + +/* Class values */ +#define RRCL_ALIAS 1 /* for CNAMES, conflicts with RRCL_RECORD */ +#define RRCL_RECORD 2 /* normal direct record */ +#define RRCL_IDEM 4 /* types that conflict with no others (MX, CNAME, ...) */ +#define RRCL_PTR 8 /* PTR */ + +/* Standard excludes for the classes */ +#define RRX_ALIAS (RRCL_RECORD|RRCL_PTR) +#define RRX_RECORD (RRCL_ALIAS|RRCL_PTR) +#define RRX_IDEM 0 +#define RRX_PTR (RRCL_ALIAS|RRCL_RECORD) + +/* There could be a separate table detailing the relationship of types, but this + * is slightly more flexible, as it allows a finer granularity of exclusion. Also, + * Membership in multiple classes could be added. + * Index by internally used RR-set indices, not RR type values! + */ +extern const struct rr_infos rr_info[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const struct rr_infos rr_info[NRRTOT] = { + {RRCL_RECORD, RRX_RECORD} /* A */, + {RRCL_IDEM, RRX_IDEM} /* NS */, + {RRCL_ALIAS, RRX_ALIAS} /* CNAME */, + {RRCL_IDEM, RRX_IDEM} /* SOA */, + {RRCL_PTR, RRX_PTR} /* PTR */, + {RRCL_IDEM, RRX_IDEM} /* MX */, + {RRCL_IDEM, RRX_IDEM} /* TXT */, + {RRCL_RECORD, RRX_RECORD} /* AAAA */, + {RRCL_IDEM, RRX_IDEM} /* MD */, + {RRCL_IDEM, RRX_IDEM} /* MF */, + {RRCL_IDEM, RRX_IDEM} /* MB */, + {RRCL_IDEM, RRX_IDEM} /* MG */, + {RRCL_IDEM, RRX_IDEM} /* MR */, + {RRCL_IDEM, RRX_IDEM} /* NULL */, + {RRCL_RECORD, RRX_RECORD} /* WKS */, + {RRCL_RECORD, RRX_RECORD} /* HINFO */, + {RRCL_IDEM, RRX_IDEM} /* MINFO */, + {RRCL_RECORD, RRX_RECORD} /* RP */, + {RRCL_RECORD, RRX_RECORD} /* AFSDB */, + {RRCL_RECORD, RRX_RECORD} /* X25 */, + {RRCL_RECORD, RRX_RECORD} /* ISDN */, + {RRCL_RECORD, RRX_RECORD} /* RT */, + {RRCL_RECORD, RRX_RECORD} /* NSAP */, + {RRCL_PTR, RRX_PTR} /* NSAP_PTR */, + {RRCL_IDEM, RRX_IDEM} /* SIG */, + {RRCL_IDEM, RRX_IDEM} /* KEY */, + {RRCL_IDEM, RRX_IDEM} /* PX */, + {RRCL_RECORD, RRX_RECORD} /* GPOS */, + {RRCL_RECORD, RRX_RECORD} /* LOC */, + {RRCL_IDEM, RRX_IDEM} /* NXT */, + {RRCL_RECORD, RRX_RECORD} /* EID */, + {RRCL_RECORD, RRX_RECORD} /* NIMLOC */, + {RRCL_RECORD, RRX_RECORD} /* SRV */, + {RRCL_RECORD, RRX_RECORD} /* ATMA */, + {RRCL_RECORD, RRX_RECORD} /* NAPTR */, + {RRCL_RECORD, RRX_RECORD} /* KX */, + {RRCL_RECORD, RRX_RECORD} /* CERT */, + {RRCL_IDEM, RRX_IDEM} /* APL */, + {RRCL_IDEM, RRX_IDEM} /* DS */, + {RRCL_IDEM, RRX_IDEM} /* SSHFP */, + {RRCL_IDEM, RRX_IDEM} /* IPSECKEY */, + {RRCL_IDEM, RRX_IDEM} /* RRSIG */, + {RRCL_IDEM, RRX_IDEM} /* NSEC */, + {RRCL_IDEM, RRX_IDEM} /* DNSKEY */, + {RRCL_IDEM, RRX_IDEM} /* DHCID */, + {RRCL_IDEM, RRX_IDEM} /* NSEC3 */, + {RRCL_IDEM, RRX_IDEM} /* NSEC3PARAM */ +}; +#endif + +int rr_tp_byname(char *name); +const char *loc2str(const void *binary, char *ascii, size_t asclen); + +#endif diff --git a/app/src/main/jni/pdnsd/src/rr_types.in b/app/src/main/jni/pdnsd/src/rr_types.in new file mode 100644 index 0000000..5ebd2f3 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/rr_types.in @@ -0,0 +1,99 @@ +# This file is part of the pdnsd package. + +# This file contains information about the RR types implemented in pdnsd +# and is used for generating rr_types.h. +# It was derived from the following source: http://www.bind9.net/dns-parameters +# +# After making modifications to this file the file rr_types.h should be regenerated! +# +# Info about the format of this file: +# Blank lines and lines starting with '#' are ignored, all other lines +# are assumed to define an RR type. Lines starting with '+' define most +# frequently used types. An RR type preceded by a '-' will not be cached +# by pdnsd. The next two fields are interpreted as the name and the value +# of the RR type, resp. A subsequent word in parenthesis will be interpreted a +# class name (used for conflict resolution). Remaining fields are ignored. +# +# Adding or removing an initial '+' can be done safely without requiring +# changes to the source code (other than regenerating rr_types.h). +# +# If you are sure that you will never use certain RR types you can disable +# caching for them and make pdnsd slightly more efficient by placing a +# '-' sign in front of the lines that define those types. +# For a list of obsolete RR types see e.g. +# http://en.wikipedia.org/wiki/List_of_DNS_record_types . +# Note that some RR types are essential for pdnsd; these are currently: +# A, NS, CNAME, SOA, PTR, MX and (if you want IPv6 support) AAAA. +# Disabling caching for these types will cause pdnsd to fail to compile +# or cause a fatal run-time error. +# +# Removing a '-' sign to enable caching can be risky if the support in +# the pdnsd code is missing or inadequate, so only do this if you really know +# what you are doing. SPF records are supported, however, so it should be safe +# to enable caching for them. + +# RR TYPE Value (class) and meaning Reference +# ----------- --------------------------------------------- --------- ++ A 1 (RECORD) a host address [RFC 1035] ++ NS 2 an authoritative name server [RFC 1035] + MD 3 a mail destination (Obsolete - use MX) [RFC 1035] + MF 4 a mail forwarder (Obsolete - use MX) [RFC 1035] ++ CNAME 5 (ALIAS) the canonical name for an alias [RFC 1035] ++ SOA 6 marks the start of a zone of authority [RFC 1035] + MB 7 a mailbox domain name (EXPERIMENTAL) [RFC 1035] + MG 8 a mail group member (EXPERIMENTAL) [RFC 1035] + MR 9 a mail rename domain name (EXPERIMENTAL) [RFC 1035] + NULL 10 a null RR (EXPERIMENTAL) [RFC 1035] + WKS 11 (RECORD) a well known service description [RFC 1035] ++ PTR 12 (PTR) a domain name pointer [RFC 1035] + HINFO 13 (RECORD) host information [RFC 1035] + MINFO 14 mailbox or mail list information [RFC 1035] ++ MX 15 mail exchange [RFC 1035] ++ TXT 16 text strings [RFC 1035] + RP 17 (RECORD) for Responsible Person [RFC 1183] + AFSDB 18 (RECORD) for AFS Data Base location [RFC 1183][RFC 5864] + X25 19 (RECORD) for X.25 PSDN address [RFC 1183] + ISDN 20 (RECORD) for ISDN address [RFC 1183] + RT 21 (RECORD) for Route Through [RFC 1183] + NSAP 22 (RECORD) for NSAP address, NSAP style A record [RFC 1706] + NSAP-PTR 23 (PTR) for domain name pointer, NSAP style [RFC 1348] + SIG 24 for security signature [RFC 4034][RFC 3755][RFC 2535] + KEY 25 for security key [RFC 4034][RFC 3755][RFC 2535] + PX 26 X.400 mail mapping information [RFC 2163] + GPOS 27 (RECORD) Geographical Position [RFC 1712] ++ AAAA 28 (RECORD) IP6 Address [RFC 3596] + LOC 29 (RECORD) Location Information [RFC 1876] + NXT 30 Next Domain - OBSOLETE [RFC 3755][RFC 2535] + EID 31 (RECORD) Endpoint Identifier [Patton] + NIMLOC 32 (RECORD) Nimrod Locator [Patton] + SRV 33 (RECORD) Server Selection [RFC 2782] + ATMA 34 (RECORD) ATM Address [ATMDOC] + NAPTR 35 (RECORD) Naming Authority Pointer [RFC 2915][RFC 2168][RFC 3403] + KX 36 (RECORD) Key Exchanger [RFC 2230] + CERT 37 (RECORD) CERT [RFC 4398] +- A6 38 A6 (Experimental) [RFC 3226][RFC 2874] +- DNAME 39 (ALIAS) DNAME [RFC 2672] +- SINK 40 SINK [Eastlake] +- OPT 41 OPT [RFC 2671] + APL 42 APL [RFC 3123] + DS 43 Delegation Signer [RFC 4034][RFC 3658] + SSHFP 44 SSH Key Fingerprint [RFC 4255] + IPSECKEY 45 IPSECKEY [RFC 4025] + RRSIG 46 RRSIG [RFC 4034][RFC 3755] + NSEC 47 NSEC [RFC 4034][RFC 3755] + DNSKEY 48 DNSKEY [RFC 4034][RFC 3755] + DHCID 49 DHCID [RFC 4701] + NSEC3 50 NSEC3 [RFC 5155] + NSEC3PARAM 51 NSEC3PARAM [RFC 5155] +# Unassigned 52-54 +- HIP 55 Host Identity Protocol [RFC 5205] +- NINFO 56 NINFO [Reid] +- RKEY 57 RKEY [Reid] +- TALINK 58 Trust Anchor LINK [Wijngaards] +# Unassigned 59-98 +- SPF 99 Sender Policy Framework [RFC 4408] +- UINFO 100 [IANA-Reserved] +- UID 101 [IANA-Reserved] +- GID 102 [IANA-Reserved] +- UNSPEC 103 [IANA-Reserved] +# Unassigned 104-248 diff --git a/app/src/main/jni/pdnsd/src/servers.c b/app/src/main/jni/pdnsd/src/servers.c new file mode 100644 index 0000000..8549865 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/servers.c @@ -0,0 +1,856 @@ +/* servers.c - manage a set of dns servers + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "error.h" +#include "servers.h" +#include "conff.h" +#include "consts.h" +#include "icmp.h" +#include "netdev.h" +#include "helpers.h" +#include "dns_query.h" + + +/* + * We may be a little over-strict with locks here. Never mind... + * Also, there may be some code-redundancy regarding uptests. It saves some locks, though. + */ + +static pthread_mutex_t servers_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t server_data_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t server_test_cond = PTHREAD_COND_INITIALIZER; +static int server_data_users = 0, server_status_ping = 0; +/* Used to notify the server status thread that it should discontinue uptests. */ +volatile int signal_interrupt=0; +#define statusintsig SIGHUP + +static short retest_flag=0; + +static char schm[32]; + +static void sigint_handler(int signum); + +/* + * Execute an individual uptest. Call with locks applied + */ +static int uptest (servparm_t *serv, int j) +{ + int ret=0, count_running_ping=0; + pdnsd_a *s_addr= PDNSD_A2_TO_A(&DA_INDEX(serv->atup_a,j).a); + + DEBUG_PDNSDA_MSG("performing uptest (type=%s) for %s\n",const_name(serv->uptest),PDNSDA2STR(s_addr)); + + /* Unlock the mutex because some of the tests may take a while. */ + ++server_data_users; + if((serv->uptest==C_PING || serv->uptest==C_QUERY) && pthread_equal(pthread_self(),servstat_thrid)) { + /* Inform other threads that a ping is in progress. */ + count_running_ping=1; + ++server_status_ping; + } + pthread_mutex_unlock(&servers_lock); + + switch (serv->uptest) { + case C_NONE: + /* Don't change */ + ret=DA_INDEX(serv->atup_a,j).is_up; + break; + case C_PING: + ret=ping(is_inaddr_any(&serv->ping_a) ? s_addr : &serv->ping_a, serv->ping_timeout,PINGREPEAT)!=-1; + break; + case C_IF: + case C_DEV: + case C_DIALD: + ret=if_up(serv->interface); +#if (TARGET==TARGET_LINUX) + if (ret!=0) { + if(serv->uptest==C_DEV) + ret=dev_up(serv->interface,serv->device); + else if (serv->uptest==C_DIALD) + ret=dev_up("diald",serv->device); + } +#endif + break; + case C_EXEC: { + pid_t pid; + + if ((pid=fork())==-1) { + DEBUG_MSG("Could not fork to perform exec uptest: %s\n",strerror(errno)); + break; + } else if (pid==0) { /* child */ + /* + * If we ran as setuid or setgid, do not inherit this to the + * command. This is just a last guard. Running pdnsd as setuid() + * or setgid() is a no-no. + */ + if (setgid(getgid()) == -1 || setuid(getuid()) == -1) { + log_error("Could not reset uid or gid: %s",strerror(errno)); + _exit(1); + } + /* Try to setuid() to a different user as specified. Good when you + don't want the test command to run as root */ + if (!run_as(serv->uptest_usr)) { + _exit(1); + } + { + struct rlimit rl; int i; + /* + * Mark all open fd's FD_CLOEXEC for paranoia reasons. + */ + if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { + log_error("getrlimit() failed: %s",strerror(errno)); + _exit(1); + } + for (i = 0; i < rl.rlim_max; i++) { + if (fcntl(i, F_SETFD, FD_CLOEXEC) == -1 && errno != EBADF) { + log_error("fcntl(F_SETFD) failed: %s",strerror(errno)); + _exit(1); + } + } + } + execl("/bin/sh", "uptest_sh","-c",serv->uptest_cmd,(char *)NULL); + _exit(1); /* failed execl */ + } else { /* parent */ + int status; + pid_t wpid = waitpid(pid,&status,0); + if (wpid==pid) { + if(WIFEXITED(status)) { + int exitstatus=WEXITSTATUS(status); + DEBUG_MSG("uptest command \"%s\" exited with status %d\n", + serv->uptest_cmd, exitstatus); + ret=(exitstatus==0); + } +#if DEBUG>0 + else if(WIFSIGNALED(status)) { + DEBUG_MSG("uptest command \"%s\" was terminated by signal %d\n", + serv->uptest_cmd, WTERMSIG(status)); + } + else { + DEBUG_MSG("status of uptest command \"%s\" is of unkown type (0x%x)\n", + serv->uptest_cmd, status); + } +#endif + } +#if DEBUG>0 + else if (wpid==-1) { + DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: " + "waitpid for pid %d failed: %s\n", + serv->uptest_cmd, pid, strerror(errno)); + } + else { + DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: " + "waitpid returned %d, expected pid %d\n", + serv->uptest_cmd, wpid, pid); + } +#endif + } + } + break; + case C_QUERY: + ret=query_uptest(s_addr, serv->port, serv->query_test_name, + serv->timeout>=global.timeout?serv->timeout:global.timeout, + PINGREPEAT); + } /* end of switch */ + + pthread_mutex_lock(&servers_lock); + if(count_running_ping) + --server_status_ping; + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + + DEBUG_PDNSDA_MSG("result of uptest for %s: %s\n", + PDNSDA2STR(s_addr), + ret?"OK":"failed"); + return ret; +} + +static int scheme_ok(servparm_t *serv) +{ + if (serv->scheme[0]) { + if (!schm[0]) { + ssize_t nschm; + int sc = open(global.scheme_file, O_RDONLY); + char *s; + if (sc<0) + return 0; + nschm = read(sc, schm, sizeof(schm)-1); + close(sc); + if (nschm < 0) + return 0; + schm[nschm] = '\0'; + s = strchr(schm, '\n'); + if (s) + *s='\0'; + } + if (fnmatch(serv->scheme, schm, 0)) + return 0; + } + return 1; +} + +/* Internal server test. Call with locks applied. + May test a single server ip or several collectively. + */ +static void retest(int i, int j) +{ + time_t s_ts; + servparm_t *srv=&DA_INDEX(servers,i); + int nsrvs=DA_NEL(srv->atup_a); + + if(!nsrvs) return; + if(j>=0) { + if(jatup_a,j); + at->is_up=0; + at->i_ts=s_ts; + } + } + else if(srv->uptest==C_NONE) { + s_ts=time(NULL); + + for(;jatup_a,j).i_ts=s_ts; + } + } + else if(srv->uptest==C_QUERY || (srv->uptest==C_PING && is_inaddr_any(&srv->ping_a))) { /* test each ip address separately */ + for(;jatup_a,j); + s_ts=time(NULL); + at->is_up=uptest(srv,j); + if(signal_interrupt) + break; + at->i_ts=s_ts; + } + } + else { /* test ip addresses collectively */ + int res; + + s_ts=time(NULL); + res=uptest(srv,j); + for(;jatup_a,j); + at->is_up=res; + if(signal_interrupt && srv->uptest==C_PING) + continue; + at->i_ts=s_ts; + } + } +} + + +/* This is called by the server status thread to discover the addresses of root servers. + Call with server_lock applied. +*/ +static addr2_array resolv_rootserver_addrs(atup_array a, int port, char edns_query, time_t timeout) +{ + addr2_array retval=NULL; + + /* Unlock the mutex because this may take a while. */ + ++server_data_users; + pthread_mutex_unlock(&servers_lock); + + retval= dns_rootserver_resolv(a,port,edns_query,timeout); + + pthread_mutex_lock(&servers_lock); + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + + return retval; +} + +/* + * Refresh the server status by pinging or testing the interface in the given interval. + * Note that you may get inaccuracies in the dimension of the ping timeout or the runtime + * of your uptest command if you have uptest=ping or uptest=exec for at least one server. + * This happens when all the uptests for the first n servers take more time than the inteval + * of n+1 (or 0 when n+1>servnum). I do not think that these delays are critical, so I did + * not to anything about that (because that may also be costly). + */ +void *servstat_thread(void *p) +{ + struct sigaction action; + int keep_testing; + + /* (void)p; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + pthread_mutex_lock(&servers_lock); + /* servstat_thrid=pthread_self(); */ + + signal_interrupt=0; + action.sa_handler = sigint_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + if(sigaction(statusintsig, &action, NULL) == 0) { + sigset_t smask; + sigemptyset(&smask); + sigaddset(&smask, statusintsig); + pthread_sigmask(SIG_UNBLOCK,&smask,NULL); + } + else { + log_warn("Cannot install signal handler for server status thread: %s\n",strerror(errno)); + } + + for(;;) { + do { + int i,n; + keep_testing=0; + retest_flag=0; + schm[0] = '\0'; + n=DA_NEL(servers); + for (i=0;irootserver==2) { + /* First get addresses of root servers. */ + addr2_array adrs; + int l, one_up=0; + + if(!scheme_ok(sp)) { + time_t now=time(NULL); + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts=now; + } else if(sp->uptest==C_PING || sp->uptest==C_QUERY) { + /* Skip ping or query tests until after discovery. */ + if(sp->interval>0) + one_up= DA_NEL(sp->atup_a); + else { + time_t now=time(NULL); + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j); + if(at->is_up || at->i_ts==0) + one_up=1; + at->i_ts=now; + } + } + } + else { + retest(i,-1); + + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).is_up) { + one_up=1; + break; + } + } + } + + if(!one_up) { + if (needs_intermittent_testing(sp)) keep_testing=1; + continue; + } + + DEBUG_MSG("Attempting to discover root servers for server section #%d.\n",i); + adrs=resolv_rootserver_addrs(sp->atup_a,sp->port,sp->edns_query,sp->timeout); + l= DA_NEL(adrs); + if(l>0) { + struct timeval now; + struct timespec timeout; + atup_array ata; + DEBUG_MSG("Filling server section #%d with %d root server addresses.\n",i,l); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + 60; /* time out after 60 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + DEBUG_MSG("Timed out while waiting for exclusive access to server data" + " to set root server addresses of server section #%d\n",i); + da_free(adrs); + keep_testing=1; + continue; + } + } + ata = DA_CREATE(atup_array, l); + if(!ata) { + log_warn("Out of memory in servstat_thread() while discovering root servers."); + da_free(adrs); + keep_testing=1; + continue; + } + for(j=0; ja = DA_INDEX(adrs,j); + at->is_up=sp->preset; + at->i_ts= sp->interval<0 ? time(NULL): 0; + } + da_free(sp->atup_a); + sp->atup_a=ata; + da_free(adrs); + /* Successfully set IP addresses for this server section. */ + sp->rootserver=1; + } + else { + DEBUG_MSG("Failed to discover root servers in servstat_thread() (server section #%d).\n",i); + if(adrs) da_free(adrs); + if(DA_NEL(sp->atup_a)) keep_testing=1; + continue; + } + } + + if (needs_testing(sp)) keep_testing=1; + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts) + goto individual_tests; + /* Test collectively */ + if(!signal_interrupt) retest(i,-1); + continue; + + individual_tests: + for(j=0; !signal_interrupt && jatup_a,j).i_ts, now; + + if (ts==0 /* Always test servers with timestamp 0 */ || + (needs_intermittent_testing(sp) && + ((now=time(NULL))-ts>sp->interval || + ts>now /* kluge for clock skew */))) + { + retest(i,j); + } + } + } + } while(!signal_interrupt && retest_flag); + + signal_interrupt=0; + + /* Break the loop and exit the thread if it is no longer needed. */ + if(!keep_testing) break; + + { + struct timeval now; + struct timespec timeout; + time_t minwait; + int i,n,retval; + + gettimeofday(&now,NULL); + minwait=3600; /* Check at least once every hour. */ + n=DA_NEL(servers); + for (i=0;iatup_a); + for(j=0;jatup_a,j).i_ts; + if(ts==0) { + /* Test servers with timestamp 0 without delay */ + if(minwait > 0) minwait=0; + } + else if(needs_intermittent_testing(sp)) { + time_t wait= ts + sp->interval - now.tv_sec; + if(wait < minwait) minwait=wait; + } + } + } + timeout.tv_sec = now.tv_sec; + if(minwait>0) + timeout.tv_sec += minwait; + timeout.tv_nsec = now.tv_usec * 1000 + 500000000; /* wait at least half a second. */ + if(timeout.tv_nsec>=1000000000) { + timeout.tv_nsec -= 1000000000; + ++timeout.tv_sec; + } + /* While we wait for a server_test_cond condition or a timeout + the servers_lock mutex is unlocked, so other threads can access + server data + */ + retval=pthread_cond_timedwait(&server_test_cond, &servers_lock, &timeout); + DEBUG_MSG("Server status thread woke up (%s signal).\n", + retval==0?"test condition":retval==ETIMEDOUT?"timer":retval==EINTR?"interrupt":"error"); + } + } + + /* server status thread no longer needed. */ + servstat_thrid=main_thrid; + pthread_mutex_unlock(&servers_lock); + DEBUG_MSG("Server status thread exiting.\n"); + return NULL; +} + +/* + * Start the server status thread. + */ +int start_servstat_thread() +{ + pthread_t stt; + + int rv=pthread_create(&stt,&attr_detached,servstat_thread,NULL); + if (rv) + log_warn("Failed to start server status thread: %s",strerror(rv)); + else { + servstat_thrid=stt; + log_info(2,"Server status thread started."); + } + return rv; +} + +/* + * This can be used to mark a server (or a list of nadr servers) up (up=1) or down (up=0), + * or to schedule an immediate retest (up=-1). + * We can't always use indices to identify a server, because we allow run-time + * configuration of server addresses, so the servers are identified by their IP addresses. + */ +void sched_server_test(pdnsd_a *sa, int nadr, int up) +{ + int k,signal_test; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + /* This obviously isn't very efficient, but nadr should be small + and anything else would introduce considerable overhead */ + for(k=0;katup_a); + for(j=0;jatup_a,j); + if(equiv_inaddr2(sak,&at->a)) { + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + DEBUG_PDNSDA_MSG("Marked server %s %s.\n",PDNSDA2STR(sak),up?"up":"down"); + } + else if(at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + } + } + } + if(signal_test) pthread_cond_signal(&server_test_cond); + + pthread_mutex_unlock(&servers_lock); +} + +/* Mark a set of servers up or down or schedule uptests. + * If i>=0 only the server section with index i is scanned, + * if i<0 all sections are scanned. + * Only sections matching label are actually set. A NULL label matches + * any section. + * up=1 or up=0 means mark server up or down, up=-1 means retest. + * + * A non-zero return value indicates an error. + */ +int mark_servers(int i, char *label, int up) +{ + int retval=0,n,signal_test; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + n=DA_NEL(servers); + if(i>=0) { + /* just one section */ + if(ilabel && !strcmp(sp->label,label))) { + int j,m=DA_NEL(sp->atup_a); + + /* If a section with undiscovered root servers is marked up, signal a test. */ + if(m && sp->rootserver>1 && up>0) signal_test=1; + + for(j=0;jatup_a,j); + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + } + else if(at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + } + } + if(signal_test) { + if(pthread_equal(servstat_thrid,main_thrid)) + retval=start_servstat_thread(); + else { + retest_flag=1; + retval=pthread_cond_signal(&server_test_cond); + } + } + + pthread_mutex_unlock(&servers_lock); + return retval; +} + +/* + * Test called by the dns query handlers to handle interval=onquery cases. + */ +void test_onquery() +{ + int i,n,signal_test; + + pthread_mutex_lock(&servers_lock); + schm[0] = '\0'; + signal_test=0; + n=DA_NEL(servers); + for (i=0;iinterval==-1) { + if(sp->rootserver<=1) + retest(i,-1); + else { + /* We leave root-server discovery to the server status thread */ + int j,m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts=0; + signal_test=1; + } + } + } + + if(signal_test) { + int rv; + if(pthread_equal(servstat_thrid,main_thrid)) + start_servstat_thread(); + else { + retest_flag=1; + if((rv=pthread_cond_signal(&server_test_cond))) { + DEBUG_MSG("test_onquery(): couldn't signal server status thread: %s\n",strerror(rv)); + } + } + } + + pthread_mutex_unlock(&servers_lock); +} + +/* non-exclusive lock, for read only access to server data. */ +void lock_server_data() +{ + pthread_mutex_lock(&servers_lock); + ++server_data_users; + pthread_mutex_unlock(&servers_lock); +} + +void unlock_server_data() +{ + pthread_mutex_lock(&servers_lock); + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + pthread_mutex_unlock(&servers_lock); +} + +/* Try to obtain an exclusive lock, needed for modifying server data. + Return 1 on success, 0 on failure (time out after tm seconds). +*/ +int exclusive_lock_server_data(int tm) +{ + struct timeval now; + struct timespec timeout; + + pthread_mutex_lock(&servers_lock); + if(server_status_ping>0 && !pthread_equal(servstat_thrid,main_thrid)) { + int err; + /* Try to interrupt server status thread to prevent delays. */ + DEBUG_MSG("Sending server status thread an interrupt signal.\n"); + if((err=pthread_kill(servstat_thrid,statusintsig))) { + DEBUG_MSG("pthread_kill failed: %s\n",strerror(err)); + } + } + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + tm; /* time out after tm seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + pthread_mutex_unlock(&servers_lock); + return 0; + } + } + return 1; +} +/* Call this to free the lock obtained with exclusive_lock_server_data(). + If retest is nonzero, the server-status thread is reactivated to check + which servers are up. This is useful in case the configuration has changed. +*/ +void exclusive_unlock_server_data(int retest) +{ + if(retest) { + if(pthread_equal(servstat_thrid,main_thrid)) + start_servstat_thread(); + else + pthread_cond_signal(&server_test_cond); + } + pthread_mutex_unlock(&servers_lock); +} + +/* + Change addresses of servers during runtime. + i is the number of the server section to change. + ar should point to an array of IP addresses (may be NULL). + up=1 or up=0 means mark server up or down afterwards, + up=-1 means retest. + + A non-zero return value indicates an error. +*/ +int change_servers(int i, addr_array ar, int up) +{ + int retval=0,j,change,signal_test; + int n; + servparm_t *sp; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + change=0; + n=DA_NEL(ar); + sp=&DA_INDEX(servers,i); + if(n != DA_NEL(sp->atup_a) || sp->rootserver>1) + change=1; + else { + int j; + for(j=0;jatup_a,j).a)) { + change=1; + break; + } + } + if(change) { + /* we need exclusive access to the server data to make the changes */ + struct timeval now; + struct timespec timeout; + atup_array ata; + + if(server_status_ping>0 && !pthread_equal(servstat_thrid,main_thrid)) { + int err; + /* Try to interrupt server status thread to prevent delays. */ + DEBUG_MSG("Sending server status thread an interrupt signal.\n"); + if((err=pthread_kill(servstat_thrid,statusintsig))) { + DEBUG_MSG("pthread_kill failed: %s\n",strerror(err)); + } + } + + DEBUG_MSG("Changing IPs of server section #%d\n",i); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + 60; /* time out after 60 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + retval=ETIMEDOUT; + goto unlock_mutex; + } + } + + ata= DA_CREATE(atup_array, n); + if(!ata) { + log_warn("Out of memory in change_servers()."); + retval=ENOMEM; + goto unlock_mutex; + } + da_free(sp->atup_a); + sp->atup_a=ata; + /* Stop trying to discover rootservers + if we set the addresses using this routine. */ + if(sp->rootserver>1) sp->rootserver=1; + } + + for(j=0; jatup_a,j); + if(change) { + SET_PDNSD_A2(&at->a, &DA_INDEX(ar,j)); + at->is_up=sp->preset; + } + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + } + else if(change || at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + + if(signal_test) { + if(pthread_equal(servstat_thrid,main_thrid)) + retval=start_servstat_thread(); + else { + retest_flag=1; + retval=pthread_cond_signal(&server_test_cond); + } + } + + unlock_mutex: + pthread_mutex_unlock(&servers_lock); + return retval; +} + + +/* + The signal handler for the signal to tell the server status thread to discontinue testing. +*/ +static void sigint_handler(int signum) +{ + signal_interrupt=1; +} diff --git a/app/src/main/jni/pdnsd/src/servers.h b/app/src/main/jni/pdnsd/src/servers.h new file mode 100644 index 0000000..fd263c0 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/servers.h @@ -0,0 +1,68 @@ +/* servers.h - manage a set of dns servers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _SERVERS_H_ +#define _SERVERS_H_ + +#include +#include "consts.h" + +/* Number of ping timeouts before we take a server offline. */ +#define PINGREPEAT 2 + +extern pthread_t servstat_thrid; +extern volatile int signal_interrupt; + + +int start_servstat_thread(void); +void sched_server_test(pdnsd_a *sa, int nadr, int up); +int mark_servers(int i, char* label, int up); +void test_onquery(void); +void lock_server_data(); +void unlock_server_data(); +int exclusive_lock_server_data(int tm); +void exclusive_unlock_server_data(int retest); +int change_servers(int i, addr_array ar, int up); + +inline static int needs_testing(servparm_t *sp) + __attribute__((always_inline)); +inline static int needs_testing(servparm_t *sp) +{ + return ((sp->interval>0 || sp->interval==-2) && (sp->uptest!=C_NONE || sp->scheme[0])); +} + +inline static int needs_intermittent_testing(servparm_t *sp) + __attribute__((always_inline)); +inline static int needs_intermittent_testing(servparm_t *sp) +{ + return (sp->interval>0 && (sp->uptest!=C_NONE || sp->scheme[0])); +} + +inline static int is_interrupted_servstat_thread() + __attribute__((always_inline)); +inline static int is_interrupted_servstat_thread() +{ + return (signal_interrupt && pthread_equal(pthread_self(),servstat_thrid)); +} + +#endif diff --git a/app/src/main/jni/pdnsd/src/sort_namevalues.pl b/app/src/main/jni/pdnsd/src/sort_namevalues.pl new file mode 100644 index 0000000..2014f49 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/sort_namevalues.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +use strict; + +my %dic; +my $maxkeylen=0; + +while(<>) { + if(/"(\w+)".*?(\w+)/) { + my $key=$1; my $val=$2; + if($dic{$key}) {die "The key \"$key\" does not have a unique value.\n"} + $dic{$key}=$val; + if(length($key)>$maxkeylen) {$maxkeylen=length($key)} + } + else {die "Can't find key-value pair in following line:\n$_\n"} +} + +my $linenr=0; +foreach my $key (sort(keys %dic)) { + if($linenr++) {print ",\n"} + printf("\t{%-*s%s}",$maxkeylen+4,"\"$key\",",$dic{$key}); +} +print "\n"; + +exit diff --git a/app/src/main/jni/pdnsd/src/status.c b/app/src/main/jni/pdnsd/src/status.c new file mode 100644 index 0000000..4240069 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/status.c @@ -0,0 +1,824 @@ +/* status.c - Allow control of a running server using a socket + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for offsetof */ +#include "ipvers.h" +#include "status.h" +#include "thread.h" +#include "cache.h" +#include "error.h" +#include "servers.h" +#include "dns_answer.h" +#include "helpers.h" +#include "conf-parser.h" + +#if !defined(HAVE_ALLOCA) && !defined(alloca) +#define alloca malloc +#endif + + +char *sock_path=NULL; +int stat_sock; + + +/* Print an error to the socket */ +static int print_serr(int rs, const char *msg) +{ + uint16_t cmd; + + DEBUG_MSG("Sending error message to control socket: '%s'\n",msg); + cmd=htons(1); + if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd) || + write_all(rs,msg,strlen(msg))<0) + { + DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno)); + return 0; + } + return 1; +} + +/* Print a success code to the socket */ +static int print_succ(int rs) +{ + uint16_t cmd; + + cmd=htons(0); + if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd)) { + DEBUG_MSG("Error writing to control socket: %s\n" + "Failed to send success code.\n",strerror(errno)); + return 0; + } + return 1; +} + +/* Read a cmd short */ +static int read_short(int fh, uint16_t *res) +{ + uint16_t cmd; + + if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) { + /* print_serr(fh,"Bad arg."); */ + return 0; + } + *res= ntohs(cmd); + return 1; +} + +/* Read a cmd long */ +static int read_long(int fh, uint32_t *res) +{ + uint32_t cmd; + + if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) { + /* print_serr(fh,"Bad arg."); */ + return 0; + } + *res= ntohl(cmd); + return 1; +} + +/* Read a string preceded by a char count. + A buffer of the right size is allocated to hold the result. + A return value of 1 means success, + -1 means the result is undefined (*res is set to NULL), + 0 means read or allocation error. +*/ +static int read_allocstring(int fh, char **res, unsigned *len) +{ + uint16_t count; + char *buf; + unsigned int nread; + + if(!read_short(fh,&count)) return 0; + if(count==(uint16_t)(~0)) {*res=NULL; return -1;} + if(!(buf=malloc(count+1))) return 0; + nread=0; + while(nread=buflen) return 0; + nread=0; + while(nread=buflen) return 0; + buf[count]='.'; buf[count+1]=0; + } +#endif + return 1; +} + +static void *status_thread (void *p) +{ + THREAD_SIGINIT; + /* (void)p; */ /* To inhibit "unused variable" warning */ + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + if (listen(stat_sock,5)==-1) { + log_warn("Error: could not listen on socket: %s.\nStatus readback will be impossible",strerror(errno)); + goto exit_thread; + } + for(;;) { + struct sockaddr_un ra; + socklen_t res=sizeof(ra); + int rs; + if ((rs=accept(stat_sock,(struct sockaddr *)&ra,&res))!=-1) { + uint16_t cmd; + DEBUG_MSG("Status socket query pending.\n"); + if (read_short(rs,&cmd)) { + /* Check magic number in command */ + if((cmd & 0xff00) == CTL_CMDVERNR) { + const char *errmsg; + cmd &= 0xff; + switch(cmd) { + case CTL_STATS: { + struct utsname nm; + DEBUG_MSG("Received STATUS query.\n"); + if(!print_succ(rs)) + break; + uname(&nm); + if(fsprintf(rs,"pdnsd-%s running on %s.\n",VERSION,nm.nodename)<0 || + report_cache_stat(rs)<0 || + report_thread_stat(rs)<0 || + report_conf_stat(rs)<0) + { + DEBUG_MSG("Error writing to control socket: %s\n" + "Failed to send status report.\n",strerror(errno)); + } + } + break; + case CTL_SERVER: { + char *label,*dnsaddr; + int indx; + uint16_t cmd2; + DEBUG_MSG("Received SERVER command.\n"); + if (read_allocstring(rs,&label,NULL)<=0) { + print_serr(rs,"Error reading server label."); + break; + } + if (!read_short(rs,&cmd2)) { + print_serr(rs,"Missing up|down|retest."); + goto free_label_break; + } + if(!read_allocstring(rs, &dnsaddr,NULL)) { + print_serr(rs,"Error reading DNS addresses."); + goto free_label_break; + } + /* Note by Paul Rombouts: + We are about to access server configuration data. + Now that the configuration can be changed during run time, + we should be using locks before accessing server config data, even if it + is read-only access. + However, as long as this is the only thread that calls reload_config_file() + it should be OK to read the server config without locks, but it is + something to keep in mind. + */ + { + char *endptr; + indx=strtol(label,&endptr,0); + if(!*endptr) { + if (indx<0 || indx>=DA_NEL(servers)) { + print_serr(rs,"Server index out of range."); + goto free_dnsaddr_label_break; + } + } + else { + if (!strcmp(label, "all")) + indx=-2; /* all servers */ + else + indx=-1; /* compare names */ + } + } + if(cmd2==CTL_S_UP || cmd2==CTL_S_DOWN || cmd2==CTL_S_RETEST) { + if(!dnsaddr) { + if (indx==-1) { + int i; + for (i=0;idomain=malloc(sz))) { + print_serr(rs,"Out of memory."); + goto free_sla_names_break; + } + memcpy(sl->domain,rhn,sz); + sl->exact=0; + sl->rule=tp; + p = q+1; + } + } + if(empty_cache(sla)) + print_succ(rs); + else + print_serr(rs,"Could not lock the cache."); + free_sla_names_break: + free_slist_array(sla); + free_names_break: + free(names); + } + break; + case CTL_DUMP: { + int rv,exact=0; + unsigned char *nm=NULL; + char buf[DNSNAMEBUFSIZE]; + unsigned char rhn[DNSNAMEBUFSIZE]; + DEBUG_MSG("Received DUMP command.\n"); + if (!(rv=read_domain(rs,buf,sizeof(buf)))) { + print_serr(rs,"Bad domain name."); + break; + } + if(rv>0) { + int sz; + exact=1; nm= ucharp buf; sz=sizeof(buf); + if(buf[0]=='.' && buf[1]) { + exact=0; ++nm; --sz; + } + if ((errmsg=parsestr2rhn(nm,sz,rhn))!=NULL) + goto bad_domain_name; + nm=rhn; + } + if(!print_succ(rs)) + break; + if((rv=dump_cache(rs,nm,exact))<0 || + (!rv && fsprintf(rs,"Could not find %s%s in the cache.\n", + exact?"":nm?"any entries matching ":"any entries", + nm?buf:"")<0)) + { + DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno)); + } + } + break; + incomplete_command: + print_serr(rs,"Malformed or incomplete command."); + break; + bad_arg: + print_serr(rs,"Bad arg."); + break; + bad_domain_name: + print_serr(rs,errmsg); + break; + bad_ttl: + print_serr(rs, "Bad TTL."); + break; + bad_flags: + print_serr(rs, "Bad cache flags."); + break; + out_of_memory: + print_serr(rs,"Out of memory."); + break; + default: + print_serr(rs,"Unknown command."); + } + } + else { + DEBUG_MSG("Incorrect magic number in status-socket command code: %02x\n",cmd>>8); + print_serr(rs,"Command code contains incompatible version number."); + } + } + else { + DEBUG_MSG("short status-socket query\n"); + print_serr(rs,"Command code missing or too short."); + } + close(rs); + usleep_r(100000); /* sleep some time. I do not want the query frequency to be too high. */ + } + else if (errno!=EINTR) { + log_warn("Failed to accept connection on status socket: %s. " + "Status readback will be impossible",strerror(errno)); + break; + } + } + + exit_thread: + stat_pipe=0; + close(stat_sock); + statsock_thrid=main_thrid; + + return NULL; +} + +/* + * Initialize the status socket + */ +void init_stat_sock() +{ + struct sockaddr_un *sa; + /* Should I include the terminating null byte in the calculation of the length parameter + for the socket address? The glibc info page "Details of Local Namespace" tells me I should not, + yet it is immediately followed by an example that contradicts that. + The SUN_LEN macro seems to be defined as + (offsetof(struct sockaddr_un, sun_path) + strlen(sa->sun_path)), + so I conclude it is not necessary to count the null byte, but it probably makes no + difference if you do. + */ + unsigned int sa_len = (offsetof(struct sockaddr_un, sun_path) + strlitlen("/pdnsd.status") + strlen(global.cache_dir)); + + sa=(struct sockaddr_un *)alloca(sa_len+1); + stpcpy(stpcpy(sa->sun_path,global.cache_dir),"/pdnsd.status"); + + if (unlink(sa->sun_path)!=0 && errno!=ENOENT) { /* Delete the socket */ + log_warn("Failed to unlink %s: %s.\nStatus readback will be disabled",sa->sun_path, strerror(errno)); + stat_pipe=0; + return; + } + if ((stat_sock=socket(PF_UNIX,SOCK_STREAM,0))==-1) { + log_warn("Failed to open socket: %s. Status readback will be impossible",strerror(errno)); + stat_pipe=0; + return; + } + sa->sun_family=AF_UNIX; +#ifdef BSD44_SOCKA + sa->sun_len=SUN_LEN(sa); +#endif + /* Early initialization, so that umask can be used race-free. */ + { + mode_t old_mask = umask((S_IRWXU|S_IRWXG|S_IRWXO)&(~global.ctl_perms)); + if (bind(stat_sock,(struct sockaddr *)sa,sa_len)==-1) { + log_warn("Error: could not bind socket: %s.\nStatus readback will be impossible",strerror(errno)); + close(stat_sock); + stat_pipe=0; + } + umask(old_mask); + } + + if(stat_pipe) sock_path= strdup(sa->sun_path); +} + +/* + * Start the status socket thread (see above) + */ +int start_stat_sock() +{ + pthread_t st; + + int rv=pthread_create(&st,&attr_detached,status_thread,NULL); + if (rv) + log_warn("Failed to start status thread. The status socket will be unuseable"); + else { + statsock_thrid=st; + log_info(2,"Status thread started."); + } + return rv; +} diff --git a/app/src/main/jni/pdnsd/src/status.h b/app/src/main/jni/pdnsd/src/status.h new file mode 100644 index 0000000..1d249f7 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/status.h @@ -0,0 +1,59 @@ +/* status.h - Make server status information accessible through a named pipe + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2004, 2008, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include +#include "conff.h" + +extern char *sock_path; +extern int stat_sock; + +/* The commands for pdnsd-ctl */ +#define CTL_CMDVERNR 0x6800 /* pdnsd-ctl command version (magic number used to check compatibility) */ + +#define CTL_MIN 1 +#define CTL_STATS 1 /* Give out stats (like the "traditional" status pipe) */ +#define CTL_SERVER 2 /* Enable or disable a server */ +#define CTL_RECORD 3 /* Delete or invalidate records */ +#define CTL_SOURCE 4 /* Read a hosts-style file */ +#define CTL_ADD 5 /* Add a record of the given type */ +#define CTL_NEG 6 /* Add a negative cached record */ +#define CTL_CONFIG 7 /* Re-read config file */ +#define CTL_INCLUDE 8 /* Read file as config file, disregarding global and server sections */ +#define CTL_EVAL 9 /* Parse string as if part of config file */ +#define CTL_EMPTY 10 /* Empty the cache */ +#define CTL_DUMP 11 /* Dump cache contents */ +#define CTL_MAX 11 + +#define CTL_S_UP 1 +#define CTL_S_DOWN 2 +#define CTL_S_RETEST 3 +#define CTL_R_DELETE 1 +#define CTL_R_INVAL 2 + +void init_stat_sock(void); +int start_stat_sock(void); + +#endif diff --git a/app/src/main/jni/pdnsd/src/test/Makefile.am b/app/src/main/jni/pdnsd/src/test/Makefile.am new file mode 100644 index 0000000..81da088 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/Makefile.am @@ -0,0 +1,35 @@ + +.PHONY: all clean distclean + +noinst_PROGRAMS = if_up is_local_addr tping random + +## Dirty trick: I demand that these objects be built; then, with the knowledge +## that the object files will end up here, I redefine the link chain. + +TESTADDSRC= +#TESTADDSRC= netdev.c error.c thread.c helpers.c icmp.c +TESTDEPS = netdev.o error.o thread.o helpers.o icmp.o + +TESTOBJS = netdev.o error.o thread.o helpers.o icmp.o + +if_up_SOURCES = if_up.c $(TESTADDSRC) +if_up_LDADD = $(TESTOBJS) @thread_CFLAGS@ +if_up_DEPENDENCIES = $(TESTDEPS) + +is_local_addr_SOURCES = is_local_addr.c $(TESTADDSRC) +is_local_addr_LDADD = $(TESTOBJS) @thread_CFLAGS@ +is_local_addr_DEPENDENCIES = $(TESTDEPS) + +tping_SOURCES = tping.c $(TESTADDSRC) +tping_LDADD = $(TESTOBJS) @thread_CFLAGS@ +tping_DEPENDENCIES = $(TESTDEPS) + +random_SOURCES = random.c $(TESTADDSRC) +random_LDADD = $(TESTOBJS) @thread_CFLAGS@ +random_DEPENDENCIES = $(TESTDEPS) + +# These are Symlinks we want to have in the package +#EXTRA_DIST = conff.h error.h helpers.h icmp.h ipvers.h netdev.h thread.h cacheing + +$(TESTOBJS): %.o: ../%.c + $(COMPILE) @thread_CFLAGS@ -c $< diff --git a/app/src/main/jni/pdnsd/src/test/Makefile.in b/app/src/main/jni/pdnsd/src/test/Makefile.in new file mode 100644 index 0000000..2ad8641 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/Makefile.in @@ -0,0 +1,464 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +noinst_PROGRAMS = if_up$(EXEEXT) is_local_addr$(EXEEXT) tping$(EXEEXT) \ + random$(EXEEXT) +subdir = src/test +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am__objects_1 = +am_if_up_OBJECTS = if_up.$(OBJEXT) $(am__objects_1) +if_up_OBJECTS = $(am_if_up_OBJECTS) +am_is_local_addr_OBJECTS = is_local_addr.$(OBJEXT) $(am__objects_1) +is_local_addr_OBJECTS = $(am_is_local_addr_OBJECTS) +am_random_OBJECTS = random.$(OBJEXT) $(am__objects_1) +random_OBJECTS = $(am_random_OBJECTS) +am_tping_OBJECTS = tping.$(OBJEXT) $(am__objects_1) +tping_OBJECTS = $(am_tping_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(if_up_SOURCES) $(is_local_addr_SOURCES) $(random_SOURCES) \ + $(tping_SOURCES) +DIST_SOURCES = $(if_up_SOURCES) $(is_local_addr_SOURCES) \ + $(random_SOURCES) $(tping_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +TESTADDSRC = +#TESTADDSRC= netdev.c error.c thread.c helpers.c icmp.c +TESTDEPS = netdev.o error.o thread.o helpers.o icmp.o +TESTOBJS = netdev.o error.o thread.o helpers.o icmp.o +if_up_SOURCES = if_up.c $(TESTADDSRC) +if_up_LDADD = $(TESTOBJS) @thread_CFLAGS@ +if_up_DEPENDENCIES = $(TESTDEPS) +is_local_addr_SOURCES = is_local_addr.c $(TESTADDSRC) +is_local_addr_LDADD = $(TESTOBJS) @thread_CFLAGS@ +is_local_addr_DEPENDENCIES = $(TESTDEPS) +tping_SOURCES = tping.c $(TESTADDSRC) +tping_LDADD = $(TESTOBJS) @thread_CFLAGS@ +tping_DEPENDENCIES = $(TESTDEPS) +random_SOURCES = random.c $(TESTADDSRC) +random_LDADD = $(TESTOBJS) @thread_CFLAGS@ +random_DEPENDENCIES = $(TESTDEPS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/test/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/test/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +if_up$(EXEEXT): $(if_up_OBJECTS) $(if_up_DEPENDENCIES) + @rm -f if_up$(EXEEXT) + $(LINK) $(if_up_OBJECTS) $(if_up_LDADD) $(LIBS) +is_local_addr$(EXEEXT): $(is_local_addr_OBJECTS) $(is_local_addr_DEPENDENCIES) + @rm -f is_local_addr$(EXEEXT) + $(LINK) $(is_local_addr_OBJECTS) $(is_local_addr_LDADD) $(LIBS) +random$(EXEEXT): $(random_OBJECTS) $(random_DEPENDENCIES) + @rm -f random$(EXEEXT) + $(LINK) $(random_OBJECTS) $(random_LDADD) $(LIBS) +tping$(EXEEXT): $(tping_OBJECTS) $(tping_DEPENDENCIES) + @rm -f tping$(EXEEXT) + $(LINK) $(tping_OBJECTS) $(tping_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/if_up.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/is_local_addr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tping.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am + + +.PHONY: all clean distclean + +# These are Symlinks we want to have in the package +#EXTRA_DIST = conff.h error.h helpers.h icmp.h ipvers.h netdev.h thread.h cacheing + +$(TESTOBJS): %.o: ../%.c + $(COMPILE) @thread_CFLAGS@ -c $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/app/src/main/jni/pdnsd/src/test/if_up.c b/app/src/main/jni/pdnsd/src/test/if_up.c new file mode 100644 index 0000000..af6ec97 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/if_up.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../netdev.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + if (argc!=2) { + printf("Usage: %s \n",argv[0]); + exit(1); + } + printf("if_up: %s - %s\n",argv[1],if_up(argv[1])?"up":"down"); + return 0; +} diff --git a/app/src/main/jni/pdnsd/src/test/is_local_addr.c b/app/src/main/jni/pdnsd/src/test/is_local_addr.c new file mode 100644 index 0000000..fda517d --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/is_local_addr.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../netdev.h" +#include "../ipvers.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + pdnsd_a a; + + if (argc!=2) { + printf("Usage: %s
    \n",argv[0]); + exit(1); + } +#ifdef ENABLE_IPV4 + if (inet_aton(argv[1],&a.ipv4)) { +# ifdef ENABLE_IPV6 + run_ipv4=1; +# endif + printf("is %s a local addr: %s\n",argv[1],is_local_addr(&a)?"yes":"no"); + return 0; + } +#endif +#ifdef ENABLE_IPV6 + if (inet_pton(AF_INET6,argv[1],&a.ipv6)) { +# ifdef ENABLE_IPV4 + run_ipv4=0; +# endif + printf("is %s a local addr: %s\n",argv[1],is_local_addr(&a)?"yes":"no"); + return 0; + } +#endif + printf("Adress invalid.\n"); + return 0; +} diff --git a/app/src/main/jni/pdnsd/src/test/random.c b/app/src/main/jni/pdnsd/src/test/random.c new file mode 100644 index 0000000..a447e80 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/random.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(void) +{ + init_rng(); + printf("%i\n",(int)get_rand16()); + free_rng(); + return 0; +} diff --git a/app/src/main/jni/pdnsd/src/test/tping.c b/app/src/main/jni/pdnsd/src/test/tping.c new file mode 100644 index 0000000..734a25f --- /dev/null +++ b/app/src/main/jni/pdnsd/src/test/tping.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../icmp.h" +#include "../ipvers.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + pdnsd_a a; + + if (argc!=2) { + printf("Usage: %s
    \n",argv[0]); + exit(1); + } +#ifdef ENABLE_IPV4 + if (inet_aton(argv[1],&a.ipv4)) { +# ifdef ENABLE_IPV6 + run_ipv4=1; +# endif + init_ping_socket(); + printf("ping (v4) echo from %s: %i\n",argv[1],ping(&a,100,2)); + return 0; + } +#endif +#ifdef ENABLE_IPV6 + if (inet_pton(AF_INET6,argv[1],&a.ipv6)) { +# ifdef ENABLE_IPV4 + run_ipv4=0; +# endif + init_ping_socket(); + printf("ping (v6) echo from %s: %i\n",argv[1],ping(&a,100,2)); + return 0; + } +#endif + printf("Adress invalid.\n"); + return 0; +} diff --git a/app/src/main/jni/pdnsd/src/thread.c b/app/src/main/jni/pdnsd/src/thread.c new file mode 100644 index 0000000..84c34f6 --- /dev/null +++ b/app/src/main/jni/pdnsd/src/thread.c @@ -0,0 +1,85 @@ +/* thread.c - Threading helpers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "error.h" +#include "helpers.h" +#include "conff.h" + + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +volatile short int waiting=0; /* Has the main thread already done sigwait() ? */ +#endif +pthread_attr_t attr_detached; +#if DEBUG>0 +pthread_key_t thrid_key; +#endif + +/* This is a handler for signals to the threads. We just hand the sigs on to the main thread. + * Note that this may result in blocked locks. We have no means to open the locks here, because in LinuxThreads + * the mutex functions are not async-signal safe. So, locks may still be active. We account for this by using + * softlocks (see below) in any functions called after sigwait from main(). */ +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +void thread_sig(int sig) +{ + if (sig==SIGTSTP || sig==SIGTTOU || sig==SIGTTIN) { + /* nonfatal signal. Ignore, because proper handling is very difficult. */ + return; + } + if (waiting) { + log_warn("Caught signal %i.",sig); + if (sig==SIGSEGV || sig==SIGILL || sig==SIGBUS) + crash_msg("A fatal signal occured."); + pthread_kill(main_thrid,SIGTERM); + pthread_exit(NULL); + } else { + crash_msg("An error occured at startup."); + _exit(1); + } +} +#endif + +/* This is now defined as an inline function in thread.h */ +#if 0 +void usleep_r(unsigned long usec) +{ +#if ((TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)) && defined(HAVE_USLEEP) + usleep(usec); +#else + struct timeval tv; + + tv.tv_sec=usec/1000000; + tv.tv_usec=usec%1000000; + select(0, NULL, NULL, NULL, tv); +#endif +} +#endif + diff --git a/app/src/main/jni/pdnsd/src/thread.h b/app/src/main/jni/pdnsd/src/thread.h new file mode 100644 index 0000000..12d17dd --- /dev/null +++ b/app/src/main/jni/pdnsd/src/thread.h @@ -0,0 +1,143 @@ +/* thread.h - Threading helpers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + pdnsd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include +#include +#include + +/* --- from main.c */ +extern sigset_t sigs_msk; +/* --- */ + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +extern volatile short int waiting; +void thread_sig(int sig); +#endif + +/* These are macros for setting up the signal handling of a new thread. They + * are needed because the LinuxThreads implementation obviously has some + * problems in signal handling, which makes the recommended solution (doing + * sigwait() in one thread and blocking the signals in all threads) impossible. + * So, for Linux, we have to install the fatal_sig handler. + * It seems to me that signal handlers in fact aren't shared between threads + * under Linux. Also, sigwait() does not seem to work as indicated in the docs */ + +/* Note added by Paul Rombouts: In the new Native POSIX Thread Library for Linux (NPTL) + signal handling has changed from per-thread signal handling to POSIX process signal handling, + which makes the recommended solution mentioned by Thomas Moestl possible. + In this case I can simply define THREAD_SIGINIT to be empty. + The signals are blocked in main() before any threads are created, + and we simply never unblock them except by calling sigwait() in main(). */ + +#if (TARGET==TARGET_LINUX) +# ifdef THREADLIB_NPTL +# define THREAD_SIGINIT +# else +# ifdef THREADLIB_LINUXTHREADS2 +# define THREAD_SIGINIT { \ + struct sigaction action; \ + pthread_sigmask(SIG_UNBLOCK,&sigs_msk,NULL); \ + action.sa_handler = thread_sig; \ + action.sa_mask = sigs_msk; \ + action.sa_flags = 0; \ + sigaction(SIGINT,&action,NULL); \ + sigaction(SIGILL,&action,NULL); \ + sigaction(SIGABRT,&action,NULL); \ + sigaction(SIGFPE,&action,NULL); \ + sigaction(SIGSEGV,&action,NULL); \ + sigaction(SIGTSTP,&action,NULL); \ + sigaction(SIGTTOU,&action,NULL); \ + sigaction(SIGTTIN,&action,NULL); \ + sigaction(SIGTERM,&action,NULL); \ + action.sa_handler = SIG_IGN; \ + sigemptyset(&action.sa_mask); \ + action.sa_flags = 0; \ + sigaction(SIGPIPE,&action,NULL); \ + } +# else +# define THREAD_SIGINIT { \ + struct sigaction action; \ + pthread_sigmask(SIG_UNBLOCK,&sigs_msk,NULL); \ + action.sa_handler = thread_sig; \ + action.sa_mask = sigs_msk; \ + action.sa_flags = 0; \ + sigaction(SIGILL,&action,NULL); \ + sigaction(SIGABRT,&action,NULL); \ + sigaction(SIGFPE,&action,NULL); \ + sigaction(SIGSEGV,&action,NULL); \ + sigaction(SIGTSTP,&action,NULL); \ + sigaction(SIGTTOU,&action,NULL); \ + sigaction(SIGTTIN,&action,NULL); \ + action.sa_handler = SIG_IGN; \ + sigemptyset(&action.sa_mask); \ + action.sa_flags = 0; \ + sigaction(SIGPIPE,&action,NULL); \ + } +# endif +# endif +#elif (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +#define THREAD_SIGINIT pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL) +#else +# error Unsupported platform! +#endif + + +/* This is a thread-safe usleep(). + Implementation of the BSD usleep function using nanosleep. +*/ +inline static int usleep_r(unsigned long useconds) + __attribute__((always_inline)); +inline static int usleep_r(unsigned long useconds) +{ + struct timespec ts = { tv_sec: (useconds / 1000000), + tv_nsec: (useconds % 1000000) * 1000ul }; + + return nanosleep(&ts, NULL); +} + +/* This is a thread-safe sleep(). + The semantics are somewhat different from the POSIX sleep function, + but it suits our purposes. +*/ +inline static int sleep_r (unsigned int seconds) + __attribute__((always_inline)); +inline static int sleep_r (unsigned int seconds) +{ + struct timespec ts = { tv_sec: seconds, tv_nsec: 0 }; + + return nanosleep(&ts, NULL); +} + + +/* Used for creating detached threads */ +extern pthread_attr_t attr_detached; + +#if DEBUG>0 +/* Key for storing private thread ID's */ +extern pthread_key_t thrid_key; +#endif + +#endif diff --git a/app/src/main/jni/pdnsd/version b/app/src/main/jni/pdnsd/version new file mode 100644 index 0000000..c400a37 --- /dev/null +++ b/app/src/main/jni/pdnsd/version @@ -0,0 +1 @@ +1.2.9b-par diff --git a/app/src/main/jni/system.cpp b/app/src/main/jni/system.cpp new file mode 100644 index 0000000..7b85f9e --- /dev/null +++ b/app/src/main/jni/system.cpp @@ -0,0 +1,133 @@ +#define LOG_TAG "Socksdroid" + +#include "jni.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0) +#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0) +#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0) + +void Java_net_typeblog_socks_system_jniclose(JNIEnv *env, jobject thiz, jint fd) { + close(fd); +} + +jint Java_net_typeblog_socks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring sock) { + int fd; + struct sockaddr_un addr; + const char *sockpath; + + if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd); + return (jint)-1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + sockpath = env->GetStringUTFChars(sock, 0); + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)-1); + env->ReleaseStringUTFChars(sock, sockpath); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd); + close(fd); + return (jint)-1; + } + + if (ancil_send_fd(fd, tun_fd)) { + LOGE("ancil_send_fd: %s", strerror(errno)); + close(fd); + return (jint)-1; + } + + close(fd); + return 0; +} + +static const char *classPathName = "net/typeblog/socks/System"; + +static JNINativeMethod method_table[] = { + { "jniclose", "(I)V", + (void*) Java_net_typeblog_socks_system_jniclose }, + { "sendfd", "(ILjava/lang/String;)I", + (void*) Java_net_typeblog_socks_system_sendfd } +}; + + + +/* + * Register several native methods for one class. + */ +static int registerNativeMethods(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = env->FindClass(className); + if (clazz == NULL) { + LOGE("Native registration unable to find class '%s'", className); + return JNI_FALSE; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s'", className); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +/* + * Register native methods for all classes we know about. + * + * returns JNI_TRUE on success. + */ +static int registerNatives(JNIEnv* env) +{ + if (!registerNativeMethods(env, classPathName, method_table, + sizeof(method_table) / sizeof(method_table[0]))) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +/* + * This is called by the VM when the shared library is first loaded. + */ + +typedef union { + JNIEnv* env; + void* venv; +} UnionJNIEnvToVoid; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + jint result = -1; + JNIEnv* env = NULL; + + LOGI("JNI_OnLoad"); + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed"); + goto bail; + } + env = uenv.env; + + if (registerNatives(env) != JNI_TRUE) { + LOGE("ERROR: registerNatives failed"); + goto bail; + } + + result = JNI_VERSION_1_4; + +bail: + return result; +} diff --git a/app/src/main/jniLibs/arm64-v8a/libpdnsd.so b/app/src/main/jniLibs/arm64-v8a/libpdnsd.so new file mode 100755 index 0000000000000000000000000000000000000000..2f40d13cf92c32570da0a56e6841ef33edfdf235 GIT binary patch literal 215632 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

    &av`G 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 F0RSqyVI}|o literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/arm64-v8a/libsystem.so b/app/src/main/jniLibs/arm64-v8a/libsystem.so new file mode 100755 index 0000000000000000000000000000000000000000..2d6fe64a2cd44bda5d57a51657c77844675cd727 GIT binary patch literal 10128 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 literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/arm64-v8a/libtun2socks.so b/app/src/main/jniLibs/arm64-v8a/libtun2socks.so new file mode 100755 index 0000000000000000000000000000000000000000..97668a48f27a14f088864d9aeff3c8d180ee3bc2 GIT binary patch literal 160232 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%0Du`NnBWsY_1c#ma7D#GS5NiK3Ld|7o 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

    u#I74j8%+CY6T`!pdVFFBBT8cty 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 literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/armeabi-v7a/libpdnsd.so b/app/src/main/jniLibs/armeabi-v7a/libpdnsd.so new file mode 100755 index 0000000000000000000000000000000000000000..3da3cf3e1eb193343fa25fe37bd0543d41be9e6a GIT binary patch literal 153640 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 literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/armeabi-v7a/libsystem.so b/app/src/main/jniLibs/armeabi-v7a/libsystem.so new file mode 100755 index 0000000000000000000000000000000000000000..e315ca4bf761c9aec7d0e00e58f7b81cdd700cf6 GIT binary patch literal 14020 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 Nfq@?;3AGm_4gm7pYJ~s* literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so b/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so new file mode 100755 index 0000000000000000000000000000000000000000..8087f4a177d4b13d76fbf929e090c05c54bd37e5 GIT binary patch literal 101276 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%0AjB~ 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 literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/x86/libpdnsd.so b/app/src/main/jniLibs/x86/libpdnsd.so new file mode 100755 index 0000000000000000000000000000000000000000..43f73fcad2b324f60c5c5f3e108c0b93ed4a736c GIT binary patch literal 198640 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 bVg3NA1=$G_17Qy)W(M$Dau5e<7l;V}#T*yD literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/x86/libsystem.so b/app/src/main/jniLibs/x86/libsystem.so new file mode 100755 index 0000000000000000000000000000000000000000..b5dd7736d6b04a69f22c4f2ac61aa3b627f3872e GIT binary patch literal 9764 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μ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 literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/x86_64/libpdnsd.so b/app/src/main/jniLibs/x86_64/libpdnsd.so new file mode 100755 index 0000000000000000000000000000000000000000..056b764df43475783ae7dcaa54bb3a2cbe7aaf45 GIT binary patch literal 219920 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%0AK*M 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⁢>Dh#v$Ndh!T1SLGIv= zS9}6rV`L90fB9QM^Q7we|NbLv02Nv<{Ac_FElLKZS#SeoJ4k7->0Tpf za(VrsB3U^8Yoks6772rue$o<~@+XO*( zJMnKTWc1*?=FxnR1r!wu;0aQ2J-OXS#bP(4FuLi{`4P1BLBaA~skBG)fj^+i6yyre z?huuLZ%#}sr9OrSUI%*gvcA{}2@(Dl5s+obsT1NPd9af}spq-|Bvil?FOHozUWgkkPTjM@97;Xys}ti{Yi$?EKqc7257z1 z$rpbsK!F5p5mz<8LWrWMDYnq5|Gj3%(PeMkN5!EaBho&IalgxbSZW9UgNUv=0F^jSkwl0a6Y+ z0~g#Z=|ydCfKHQ42zCq$_`e0b&f!IlD`XbMvGZv66tGRLm%u~!+2FJH8DK|9?@)&X z@QdAXC~k5E^^I>bpolp({srxSFXac7`~lw_n3(um*MZtdy{w`;AW>S9>e&33(W94j zD)@Le-gO{~-{pj3^Is;9Ue*U7Zm%p0=sY@pmm5$6J@{R2g0=Cx+{BVV1#>}R59$1M z-g_Yw$H3s&_!rbL=5N^`0opnLmx;d>w9^WdI@K8D)qsXY+&REEbb%&BK>-hm25|6q`=}&1HlGv#h4~AxH&MeJIk^8i;|T6Q z+dx$=JhsWXhSY&;0=_DUHG^4sDRecd-R$J+>8^oGh`J&ImUBXy^SG?q|W{K_0!P@1dr7biV4m`Qm*v14HLsNNOkrZQoQ; zX9S0^6?k>X97xN)^)~282+%p)kUoh=H#2zk5om?i5k~MueHkDD15o}3ty=y6|9_{C zibJo%|4tW`5Xcm{fJgR$43F*-W{xf~@KV>7OC{2vW#8ZVx7~uQP?Kl$V7w1HO{(Qm z=_^p{hl7862(uJu_l$%`a}BcwLkUZV4|4{HZ2@6tg8JR8{LP@y1GT_F$q#hNSBnH_ zM@2VZ>q-7TUeKzkUK?i5=HncWoo79pk6FBC_v|+JY(AFZ*?jcBXLsEN&^nPR;A-y0 z@BQG3JW%1!P{QTg`nL4)OHiI^Jy6O43MqM}l2?x3+#WHOKK5*``@m2l>d3#%KcNwP z?Mn%Z<%u#D#}4-dPtf(m2SHMaNKzXdJKPgNV`r_^pqZIckl$jPYg9sv%ELglp@K)R z=&2A;PVn*i=h6I=y<8YnqJtJ=FnRRyoC2NM@mdygk`p+D%H=(}LzojhS}&D|g3fUQ zDY1l{-Ok_o3N#Z_qmsc`%9GYy!z{tT-vVB@)eXA78gvOMXtOeZYbnXA3uuQnWQinyA2+BGH3OU$ zTn)c@W*^G{4GFVKeLKv^-wHbU$F=o#iJxb;4=ZR5T8WECw>~PSc~ekOy2d zcH6lco^n{9YTp-bE$ln5PDT9u(YCfpoVR^jv$W<44oUvjzd{TQpaiGR#NPwj58m#py-h zl1~Jryt@IU99;h!KvorPVg#KE0m_z*Yzz!O$saws3phXySpzOqdsM(0x@%MA7McF;*R4K*q%4E!zCpsd)-+Pelco9O`F45k1opE>xqIb=f4PHL$J zo%Y=Aqax8I2|B(#hl7CubbLD~6-e-J``LVmk$;;HV%_k`(pR99+d*A%(9$nRdI#M% z05SrU&nz@Al-Ppug9Mn@a=Szy!2=!e4mtMm6nKq$XNZbL^8v;Z9*`0X{%w*iw@Yt( zfXZ{wT71y)&7heqk8be(nu}n*kBUVXXNQl9fd{yZ$$*qa8Xnm`DjMLGQ4Sz`J$$-r zI5@gM*~x;vG|1KP8|d6b18|>ZD+8z*bd1rZ^DF4sMEw_UyZ-&}JPZmL(BUE)FYb2z z`)_%;RK~IS81&Rcm(CBaT{`SwSAzR0+d!%{KjK2>F06!*JoS0OzyiU~vynvC7{vi;aPy zE0D*d`5;frr4lKR&U5_RE_w9wh=G=`@^1?fWAx}e*K(=!WmhC4NC=b)^w<~}__uxb z=oK*p#S{Ow7(+&n&WkO#OJBYeMdWl)gBz3qL6_Ko@~q)WpWaMHU(2ujz01LEKvtnu zprqFTlJw{)CDZ(aY+y64@6oFv_YIr?-QOsS<9e zKUxoz@PQWp`&fRh+4~YS%L4aOYab|2g6_1ijsevlfuK%ZcZ&)nBe|$RjwAwg1wkpy z0GjkcE7~BQbTGUP^PK^j?+id5g82^2!Rk8$=xM`n-{q|!;Jdfm@c0fq0st?EK@C?A zsP7#3d%(WyWmQ=XDT?`9K#LAsI$uLBm+57_EDM?`(QO1#{4Ot0YAA40d2uinQcCx- z9$${+%$ghE>I_tKL8`NvvEU*UWCB__|34mN0%RVv^BuI9{uKaOx(DjO8XkCIJ%<5w z1BwDesVdlMB|NZ7x8oP69|RiH28qZs@%LnLgSCRk@IZ0Ik=9%x0b0BWN<5%j+dTd^ z|1{!n_XLe}gATdj-|ir_g0VE%Bl)C9ujmIaP?-(A_e>JnumiQ&4k~ywTQcys3bHUT zyjFuW@5<#M3c7>9tvdlk>yE$mC3GG{m4UzYE|jg#$lrevRLS=8&R7Pr$I~IL`6pwE zei~>&kyu*uPci;h(6!qQpmo=!5-*o9gFWuSPzv_38UugJM2P-~WuV~pv;dWC6)PA^ z)4_iA=ym`VF(r}U%XV5%mIRPmwA6x*-NaF}q<{(yq|D#|wi8rYf$}K0LeYR$TcCsl z7VMG)xlp6!c1Z<+dJC)^tl08UnKy!mQelA%LR459&)S~Dg{+mieQJLRW3Xzf*#$l$_2DM6&m~et^YtvrXPWdQPW` zm1E#<*$B&p4IWY)FBgM&I5>hmo?EAR22EB81Qdr@#uvXuWDd9-i5B9Vh(!M zXgT-ReFv#8vk8V#lkJgj?Eufv^u$wbMdpr4C z&VUoX1NanCP+`a4vI{hV1ZmVOfX0hKlQE#}Nu4e#1>go3xDf(6(c7apKmz1Bi!KMA z4i^=HE~d_d4WM<3R~vpZ6-jw?UgY0)z@t}(5!5{3-{!)|=+XJIicKYGLc*vvmfCsVN;6T~s_6VH-d}ZnuC&3^?Xm{Xo4h z&^_>|rJsQZXl-rNB+$xCZO|y92KYSOSkS#;CqX??P%eJ)buD5Ph`;3_=(vk+NJc?6 z4U#)NtRb5R__K#!4^w7XC_5bpm!kHzz2XdO$Orj-BU0DVf8k zx3Ivo`S?H3Xlfn8$DooI)b4vB2 zFuaHlMzjxmO}~K7AcXW!uDOAB(fuz0t@%z`0Cp>6xbXP@wAKTqO}?%FOP{!a)-?rq zb+>#0&oqEmk%)lH4-Ugiph3tfhb%Ah_c4OD9Go>g2^Kl%k$o}4r`JXRG#KfjdDgSr z9JC?a8&purGnG1f^s;`Q4~blU@CEK3y{xmQFfw>`%X~ofG1y=7p&)<3mXn z4>C%bp@a*xt3rPEzyGgGVTrut4P=-T99SZ&!3iA1W+=Jr(aTy5wYS98v)djXOkis) zf}0C><{TO2P| zbpQMRngf!CO2BmphX-iY98--}=TWc*kKTv^kZVBaBvf^SZr(i%c3tT^*cLAEEw>ym ze9J-Wv9^HwWT5*;H*CrVAsQrq33DYzyB{? z9{>OUVs#nlh!*&UOO*B3phMVvJJ0xb9s||A0v?@LJ3pgr$^f0%cHBh;w49#-bQ+7! z3!nYa)w!UX<~%!(esd6F;%^0w;raB6PM^yNS}g-x0he*wyd}=x|C_t$ga@;?fT!gdpU%e~{Qft6Tc3a?!aO_gfGSdt z<^vp{{oo#!uSz!?p7a3?>})^2f{DKsbkUh(Bj~sT2L9HM%nS^jzYo4<`hL0jG2_dB z%nS@We*I@)c*zZ_h^)!0FOK|kj`Ghv2s-#g!=smV zrVXfo@&E-FJAXUqWLJ+~p0g65t>KI>U;YCfvupU)5VShhih;icbn<~C|F)x`W|x9v z=Q)pFUR^zOPffc3@|25;gd_iU&_(3mbU`XgR19{3P7-`k4wgRX!oU5nEC2R$j-9ta zaijSf6g9o9D(^rez`CzNJ8Ae`UwHDn+<~m3G3{djg%CItK6l@g$R z>V_v@ZvFfJf3G*FKVKf}k$e)={sdja&fhKuD&u>3j=};Ml+i%3oa@=`3L3&GN%3f| zP+%yD_3RF1=;AUw&~mawlz-bt{%t-+u#47N7+X%3z6M42>x#zTpeW*R0qrGh{QVcS zGY7Puh5>Y6FK7ygzr77&BRoo{f+j<{YZ>-|w>7K+9gEX?phUk*!0=?tr4nVtZUFFk z^q=^*g%~h;Fn$0XN#1af5j4Bz*ukUX)A{~IUK?mv+4~n^MbI;2Kzpws!yufXI#(bK z{fuK!iNoL84r(Z191_{<@uT^tTDdPQ{9Pd7@6-7eGGkL>=Gl4MqZ>UIKmiU8`A*Pc zUiR{8q~!a$(x>yQtKol8k#X|12k4ekNSHt-`4P$dG-wV5R0hFS8y;|MJPbP1&$0P8 zOF3`z0};d9puHkf55ER2g|mDE%0_99%~lNjt*`(5|NpZ2@BjbJ2Y+}Re8}v{>>J_H z&FE?QhJWrsB@YSyZ6{wY`3;tC{;9^_-Vd@3lFIm7KzGbTd1yeaMa!`sx>@0VM z+E-!&u?y;6y)+kYgbn*Dz$Lf;ClBUA0Z+>>KAjIe`2Ej;ayY-sInT~B{M#J_KpCQ3 z*`t>Qni*ca)9CtnKw`v3n$RzAx8Odg$2L1!>IhB}4> zdo)6?5bd@34;pZ8odG&Vw)1S~)Ev;*Yv)!0P}`*Q4^o}#dcotkE2wMC;Bnj))cRyF z{QrWz08|_v1a)5-JUUxJOMk%S_kRJ<@(2-?)=T_-p9Me_MehwzucMb$bTX*l+5v9e zclJI22_|=dHn(^rfAZ{g0bL8)x&Y*uUKbT5kIvQ$AQhb_K(npj1x;7kdE%*;wxWT{x>dw1%fXLQ>|DZz-JUYS6FR+LKNaVjy=TZ-_ z->0qsTiLq-Ol|!DKKOTS0%%TCyW4d_r|Xp0yb#)_8!YV6z4QlYA6_R9@`0-!y)4`w zmLEJiTVH@KB=A3EdA!qgf=B0Eh5!Hm^Y?;wFL@+i2Mu7iYJlRP*CxpG;B!_F%Ud3u zQyu>Q|6j}N->YNe*?d&#CFm&E&e|y+y{zx1K&oT!-WVI65*0|(xAVS7Z|Q*-T3(=l zhAeaGHSPb!!0Pq^@J zOH_Alc~YvG)?6vW&)+(O8?^HNNkgS7L#gP?UT(yGDOdh&Y3e?pJ0J^N2@tZ39DzQ#Ux_I9eXz?*pCZ;nQ8(0BX-hHh?OH?=Kv4Kt060 zpmg)X6v76jsu!AIHrQbv-L5Ml;27?U%r%aF%FaxB>1Jv60?RaerIlsuGyLN?R=aCmj>Op#HSG-sa z)?7Q|#iDGGGylJs1r`9C2FtRrEDSajlv}~UR+l9s2Lr>4?H2H;dAa)U|Nn?2HUTw#b=OYu=muwnG{@#z z1`hry2RypL$=>oh{}jk}Wya&6c{UI0si6G9-*XE*h5+6@TEGdi8C=}hf-*RNOFAb5 z!|PQD>(VB4$4=56y$n{NT}f#_<0O zGfm&` z#QgsM-|+v7j4V*v=mq;m^PmsoN008Qpwh_0dMc<`tTctJsXD$&)YyQtvD)8C{wjZ&%N{yjJ4}6_D*g0E4 zAy;$KqkAg2kmRr739BE@;24M`vt@f3Jy+ zXY(-*-{iNT^R0ZW*MjV-(FQL{*y+s)u2_0anKyyj^`dt^Gcfq{vabEg!0^J(n1SKN zO*@c4Hls&(E67*fEH@mj-7HGCI`VIKlj7fhq4^h6iE!FZONQoO%q4uyzgSB+U$%nA zgm&66cyvz%=}Bw#xy8oclF!7z&ML4Kz2304wL2LXvVbw|K7Y2qGH%vg;+joUW zckBX>?!Dmp61kEKZV zwFYhDgYHJu2Zij(*Fp#{rcH3<2WjEocarfWtnfYo>Ntb7IPy<9?#Ou@Qha;Ditpq6 zeV|pD9-VW+A=4e(02-&53>x3-yzkKs7JX5Y2FfFl!aM`Q29>riV!&*yrML>H6qg6D z{hJFaqCGn2g0%Q_mUg`MgXIU1RT63cKnDRMwKd@C)F9>e@fR#0=XBSuc=0+FeOMpvlHz~+Ub!AYweY6J>sr)UE zVdXX~Vws#kr6(+6r+i{yc=6wef#KyYlmgqMJGKLwDPAkPbb=EXtZ@LzE8t`dQU>0i z4m$sUf69UG(kYhL;3fD6loGrW)S1v{1|4?06jb>*HveIUTEXA49~A$Me?Y}K)EfSl zUQnq%1C$eey7z*LA|LC$pkf7DAoKS&egidkk+Z!=_gqk;Fl_?=cIQJNHPM63x|6uM0JBhzVl#_u0QsK3Ny1Fk}LG@rS z>&-6gmP?{oKKTO!!wWHx<=vo0ST5m4SSTd>UIN(y$-ct;9bDkKYVY$cCrdeBwy}cN z#CV@a&z4Ubs?Rc@w5eQSnes?ooloalP=@sB&TRnowV6P}gq`nwy1}9^9w&kF0VG3S zhp<7p=EZ3+8*7FX0%b^EpU$b)RGXt#pvO6yioMcExYp+@E9{Li2wJGP_K zbxJq50%$z|P5s@m6Zrcdurn}N#!lhyiU0op|I4D!pymkJH6GpIj*qzA|996;;qP(${Qv(;&@EIR-Qdc#({;jYS&wet4$sab zuWdjKpKfpleytDf`#_!K0V-laWtn~{G;GBZ|NRFYGQ+^|S_X8R65K$KPTvkBT|S*) z13|-_8laN%T?wdTdH=<=1du_H#Bd722F3A1Lb#3Ubonl=(fxj0t@&LY{0CHxu zM|Ur{dg_${MTL?}cj*L2&3}&jK_kMBdq5|;ASxGd`NhATnScKY&?zGZj{J+y7xQ;l z%e_!f`uG1eH(1`0fAN`OQIGCwvlotFhg9pmut@m#pMN_u?DQAbwByX|phKe*9Gj2* zFJ0@>?K{C0bao-Qru6AfZQ$=qV`N}(?L6Vx>+m0Z)etjik4-V3;U&Xw9<3)!6JLUQ z#-6?Vy+DhzI}dnho&ZVl%QG0>ZrBe>7L5G!4>Nu?{MN7^R7^2C@XtTSc+c>5L;V7{ zj6GD;qxlevN8@qO30NMM7fSbn1{icadRc-%y=ly<+dC;-> zA!GAT#!_y>ONO_7TThk-z0UUR1)sO&3--xv`1w{16$_YZczu#DHy>u~_GohIY3!`qTOlPRe~U6B0|TTeTEWKO0$Q2{8WIOxPUz7qJK+rj z!%NVf6_0LMBdG-xdf+m*RM(@IH4ko34tRXrwEGPMgCXd+uszxg3@8;*FRLR|afyRZ z_g+Ya_n+CLmvt^^X;-i8vNsG29{eshKnKI`n*++`pd}H-Py<1O;@aP72T0J^lXLxjj$4xvsT^BTi zTLQfG+2FmJLMs?cBRrdHR3aF_^BOy#^BM;|jvqV#8dtdlZx1#9VlU_N>@~UK(R@te zH3w+pc!USzIgf5LkJfJ`EDe4V9N^YM^Fh#Jn9}pFIYIdfv};_*qq`S0Km|&_?2i2V z+~hhLJ-TCOfVaMDfR4JbX5ep;2j`gD10LP30v?^NCww|T_;i=9aP3M;QL! z8!A*7O65GdT{{{o)EG)dK;8N#&>$A5)XIzjpW)`xy%#hX0lNIo@c#>ySkSB(=s0rU z6+XSb2RuMcN2Gxf*UlgROBDh@!xSeMgSWgYcy=CdJy0U=*!UkbG+!bHn&E8y23ld; z_#bq#3V$o;I8VohKmSWaK?4O{9tjOU|Cg}3SUfCdap_1%=mNL>8-D#S5r)Vog5+5& zE*3L)1SEo{(gZ*bKba4zufD(77!4{UYiGPz0bwk7F+cj>f5+Z-$f&p5 zt3n3{IM_fN{9ej~#cF4~I35M|>4F#gqCiL4gO(t^bOuYtHh|8BgO0?l`w6;O;v0X< zvw#2pJ3<1zR0ve@znGc_Hetn!8jw-FtaBS7B}0k52dKf@TMfz{8z9-^rWq)E9BQam z0X6ZwUxIQHuQ7?;gB3hP@$x}$fKKeYXfMP!2w2( zPS#Zoj4-{;Ke+hYdq6h!n#Ml|yDto63uv0yaYrYp8+G8tCKU#Tm+7E$?II#UXYqrM zqGdom&kDTwsk25U0(9i>31$YQ>xe)n+%bc0-)N`@Ew9pros7~@FVDc=BFD+V@Xb+( zk-t?0)PC>04%(+;;L>>%l=?u|2K`{=Zw4RN-MvNS0%&1ny$S>9&U6dtR+8>=4e(a= zs~n((1>g8v&VU%bc@m%%Ck^1@t_OTE2B<+-(G8xP2MIIqgLk)gw}2&E50uvX^e#~W z$@p}>25q0Z@6ipuyYA5oCnQ~^5%BV;+xdqF<6+R?9O$eh&?y?A%~B24|4W{L)-!=Z z!K1qX)Ee>Wd<5DA2dW8PeCeYoIqp#ENsQ4mw28NeQz>&{-tqzimK`RD8E-#7o04L=Vl>$gw<~0GW=s4ohT@60O z02EN*3GNpqpdJC}#5c$V6JSA5SjQNGG=Mg{WIux>b5JmJ-h6Rc1$;bJ-&QsT2G8C& z3E$Rlp8PKNnveeP%uxve)rFwo_fZjm9s317SQ&Kgi3ez-ki^SNpp|12KpUwAK*uS9 zmWS*IP2_dfsAzbC)+I1_HXr2yO~Xon6e&1%xiWOPsPMaV{s3Kq;sT1D1I!EzucZ)8 zIM98l8r>eCTQ*%hx*Y^Sx)ZuwRQN4i8A?-J4WGDlm8kH$wtOpb1lEvRh9Fgr4afeMUU%ty;L=sX%HV2M!pcyx=e1aKjfx8+ ze~TaJfD8BlANW4j1W**Ug6}H?-T4ka<^^1P*Qj{7bna2<0Hx>d78Q`|LA#ql_k0=t ze{nDjQpG?|{Bs14^7(YW10_1pZ743F)0MIq0zmsnLHB_LyojF*sc8^5VQ_#3bU^D3 zKq&x}CP6o1@VA0Ct$B1iOSpoXmk0P;WWX+P0NoJN^=;L>M zT_F9vtg@A$`y~(29(L7bo;UY4RvU1T^abNwY6DfW?nM#ovRLH-QrPiy2^X$TIg{Q-vp>Qu>uo z=ffBLN}!VcCTjX|{NcfP5Klr&`E=(ifHu~(tO5mHr;myP=v;6O(9T0pCXj&Sr5F`~ms`Lq>zsf1 z^ky?6NnkF=+oZ_-KXKf`$iP{`&p@za!Yw zpz&Fd$A6?jdVLRqKnK)+<8N65D&4wURBnJ8+t5UK7__;sTf?>W8-E{&?V0@6we!0x z@@3K>^SpXxwsbCK{w({ua@BppjZarC|=g}?L zdZ|v`Bl(g?^Fbxhg+~qyY0Z`#rR?36udJ<0k|D$72NOIjuhl#Qm5K*JHi8Onk8TeE z7yj)I5{}IeR=RX~D7YG4^5_-4SIWrHP?gBw&{d(xKjn}|cYy(@)f|!5T*n2S@Z@(r z_Sy@PqCI+94^}{uB7Y0pf8@EFE#P|c#V=jt~># zG+2?MV!_|q3R>M(k8oYu1XshipeXDI-8SaY9it-f+Q*}pl@IFV63Fr^W)!yXuVzP;L*!k2~}NU2=X^LmYR>Vz)BfN{km2M>^hHL z)AbJ^)#Zy-vLGXo+SM&84a^J-r6S-v^FU25q?YxIJ^rBOZio;7#MvxQhnpY&r(Woptgay{!CYka#H(^5`}F ze4l{(Q9+iqxrbR3r9cDM%LpX9Wjob`2wE3?jo&k`TG(X85nwf z|9fe40bS?|KJL*Nw7B)XM{oJR=7-E4&HtE-Ex-j| z>q-7r(0xiTg+Mu_msPG5;$i+4@M%T8rh>54lOxT*@bWB3mF9V{R>K1b&+^MNctHHY zFW|ze!7t#$YT?K)=)>*;y55Z41WD2ZBpCsgOaV)VAW3F`Br6>G1$)>*y#la&30%H~ z6*L&dFVMpZTJHf?1-fC!kzcTd9dtOgNAocjkLDK*9+uzvTR{s)AY)1*ttU&&JPtl% z^=LlF;n93R0W`)m!J~UODBSr4Sj_nae9Q%)q2u~PL`Iw1i8pV^AzX?nd8v<_^(IzZcvPSbk}_6@aVMpfW#N@ z=&bp`fg~US5)eQV09BhGK)0EH@Mu1+;L%&g;$iuzbfri0Q3a3A7ohflhDWc+SI}`= zpaPu%rj_x!N4L#)uyvpdtt~&6KIPxuJPULdg=42X2miiikW5f02eX2jDn2R}h|&Xe?+hzwn$JZg#kKVSf1imUsKhz#*?HZw^P5NKU!UF- zl^WQ^p00*(K`UcDKwBulw_ZAcI%WZg6f^;LNBl33&YPga<|eR!mb5;6aisz@`@r9N z9<*$yTYCrSz!s0@zg+xHpj)h6I&)M+JerSw^XSy}>HO{6ZSTmx&EB&&oC$QI1EWW$ zuq*$zawgB-a28koZRyMq0e==p{%zr`uKe5F**ts8Ib8X-#j|^K8awiDE9dYy_>9B1 zH=oOue_J@GC+8=hPHi8{tNcxVAO{?L&f&p%-tpiw4p+`+X|4Z3WxGR3WLooo%~D@b zo%w(j6bgsB8G1b!JGoj9xOC^Ji14?7R^xf}vhFX0WiMUOL8zd~xL(tTFF=8N`2`o~ z442MZFF|LdrcFp|{%>3csvEkYdPSLk%ewy(s61o6 zbBTe$hu;O(zI^|JfuZxIN9SG8ZOm)MLBaV8R4ak@U8@*?E)oa;#}U|xFE@aiA0YEe zyFu%E1VIk1sraYI-wqln^gQ@b!sFm00Uyp&o(CUrcyeCwhQaq^X>fgvYCy6p<#~-D3LIL=D1i>3qWN#Xrn@j z7^v3t=r#TR3=*!J#6UICEl}*dfW%IkOQ(S&|2B7)gAZ8vxA`(RKjh~;nAYh9N;K}Q zX`N1>^x@8S;01e!6PrgbD+j{x3Q)RYJNS?Tl!DkfPklf1zaBImTA~tAq5!pcIn-iH zsKvdk$MYesb8WqZ6a%0PQeq9-C1D2Dx$GGzmae?`FAAa`y_Q5|5F~xwB`T1Z^U)Oc zWfXU8KFSC>+C7E8XFF&{b2Bdk!#+W9hHw7G#NYH5G{e~$18Rt0cH{@0xC0tPVF1~1 z)w9>>AE-qP+ONR_>Jw)`@*$`rgoqT-!bwo1c$9)8rS&rl1H(S>ITX#{3r0F`@wZ%K zWnk#`QQ`6JeD(4Lsyu%SXgnJt`RnBg(2Xe&=Y*(efYOiz%+45<43ExRKD{}hWc3m` zIh-qj*xbVe+K*SGQsdXlV&mC(0F+#yxqZ z7#LibYgB5wLH(2NJt{Xq38QxlI6=8~{&@M06J(EzN)4!mg)9lS#izGN1!VL8Eh-mS z85sVTs5rc+;Rmk?695e#c(5}tbRGn!S6k2ovTx@v{ua<^(@Rr!)VK)*b;sd>ff765 zkiiu{KD{+689trAU||K?tm=kw_GVD0o#sqwMA$lnYq7C=iKco{svy8zibT|hDy_?xFe)cde9bRO{O zwc!BWeGD28sqyK&>ZAFWzX^2ev`6P#KOx5kk5`5L3}0yI_e#@TmVP#%S@CIh1d(aoB5kZFYe@(4>q;b5Oj@f zjaM&=jRz|zexVKkZ8bXV)A`b~^8m;RPGH6SEg6`B3w8oHjdl8{@PI3am-oT*0FdnG z+j$jgCfrlVsr*GD*mJFuL6bt@#22Dc;L>Zuv=1~v=-BulbcGUs+X2w92Q;fyF(M@> zHvZ<{43KR25_BswJbIb=n?PGsT=}=jzKBgF3nVE!Chn0cXF?TfUvIUw#E`dFZ?iY7A)jc3yYn-~I_S#5WVF+>Q}cHvIyJ zqi5&sm&X_w81{kAM^1BW{>8%I0&0nY=Oy8S0{krt85kH|mw^+TM<=M9b7e{EYysbM z=Gg7ZfKufBf)#lJV5ee>k~VPp`0zz&0XRxp7@06~k}v42_FJHc2G^h-otIm`@wX;{ z23fmfR3NqN%XsiS1gyYz>AdOFdD)eJ`yo)z(7?BwAH96>?7WIxs;Gh1mNp*|XgyFO zgHq)#py9q6$L0e}FC{_E|IYW_IVv?iy)4>3o!>x3P$#(5)qxtT1lquZ znlZuo0FgCbo&=4e^xA;ufo>ZxEH|}ZGznn zEpT7)x5R@ML4(dc!xq1w>`Re<|No=ad96G@5uw9| z&GFdW9Sm8%0*X>uLr{E0 zym(;y@4rjuOZ3Qu9Ezae+xZhYCP7VckLDu+j-5wA;km^YG*V?;UEA1 zzvcv`Qm`o4X0Xd3ap?E=|9?;xb?If%0F|M{TCEGWn!g3K4H4OTu*qQCpl+!6`Tze* z(0X)8s5+x2Fb`1koDbBb@KNCbYkTPqF1Tty&0AP0;Mi-!-h7-Hl&myfoVWf5P5Pkb ztTkvG8)za7bo5a-1OGM;CdUq6CjRXVX^x$)44%EU%&@fO)A{SgBI|$uy?br+JwV1G z5>>Y~t^yuZK4_qo54xaJK)|W$IH-V#vj(NA|NJcnLBqa{;B7Sgt?R)HeQGe00yx)# z(!<~1|Np;y4T=JAp^>BF0P+92-_U~FmGK9$F)|x8<_}2>^2o6PHW3`gA3XQq-~azXEo1@1Z!hit{s$epDF8l50MwO2n&P%@QJDZ*$k-|fiZsyC zgpEgLure?p4bL_o`_%l8g})tqtV=KJ@t+I~9?3i#JQxplyQuJVx~K^Je+U|T0afgv z`>zrq#aGF!&ig){mtXi-fb!iT{_UqddQEF?LI;RDK@+&8|G_R6fa>Ih>f~>U0i70l z|Ah!dt4FWtsT&LoufvdRd6)pP1+t8y^Ztt$#tSHrijIYFIu@c1Bj@E=M2i`k;!n`R%lD*kb`{8A^-e3%hZ85TWx zxd@cdnNw6)Tsj{^=f6CTyQqM6wlZ|y0IdK9-QOm_#E{nMq9Wwc%W4&&^$CEHmzM$6iNj_ zd)AL=AZ39UQsyZ82@#tKo8Lr$XD7aRbpH0}w)bfL4{G9QbcU#ifR=hF@V7hxSA*=W z;A86~`1@{y_?0S*kHnmz!$+r}DnmrHjI=%l)IaC04e z;!=!?1?aWy>f;uFL#-~GE_(o4-~rmY4qCO50B*##sDMsi_Shl7z`$St@_1*93aH!%Ep-CXpostX;4K?f~(Fw0I22Av%6qR!;se~^XX3k`br zs6gl~Dxmq07mXmv)&nIb9=)RTE`v%7$Q9q9^(7z$`-H*gH9%GIHUDL<74zuj?Yqpt z@LKm+3;1ePhUUM_HLprJJ$iW=)-y1?7H$5^0=g6zY#eV1h=MGN^8zt@r+_tqa`OEb zpFjq8k`W%@!;b#FU@`}Ffd0SuWdu6(=tl{+kL8aNwuXxTTqV5wL1)lx{8U6bYS^w-921;z82=eUw zMw_GtisRP*C50Z4zyKW-14YM0`5Gy?AE| z>P=X`U^7GWCCCo`*6pCw)p!Ktn`p1eHoj zptY2}qN^@K5(y(H+4e%Na@Yr2_zey(aQS5cR>^v-l98d?;zd8mIla6I5g>H$>WQnl^!d+lS5{9+?;Jx);cM@~=PO zaqxvwFH1w`PtAj!A1*)eIQUAT^QY#;i!T{39(>7~ebExc^XUBKaru!W<8e>MtIfw5 zL6&Jac7AjGeh=gTiO#=>I-&E0N9GBT;T{KHDs;ZmJazFEGAIl7m@;XofsR62MB}(slbc6QeLs!!ocyxo# zV&k`RWhg!DYWN>k%a-IiHXmn9o8a2{2c%xXiGQ1o!i#lAph_0Bz)qFn#X=)+E!zuH z2(D%?gH{%U`T;IoCX6oNb#+@`W`mZC9{`1#0BE+A-J?^`r!$koqxpyhQY8a&Hh5i! z0LV52(BhkGuW$3qGdP9?2S6tN_k&B}8a;4Ny+q!lo7zy>I)IS!5-c09^Dq8i{3jqJi6lrUU-2_?7R_u7~CE~Z4ZN! z2Pl0)k|t<5j$8UtvqfRKOY!EpJI$W=1F{pg%WjTll?g>7YMI4Zu*+HpK0hA5} zO0z(bEC626iAa5+uAnwv3TQd!lac~R1K)wcwevklu>>gnS-j9QfTcfMh8Kzk;PfX8 zQtH@n z)dDZB^A0MH;o>o-tQB~ilB4Vo|F z0e5miw}3H#cSP}S2nM-8}97V#_@CjJnqr3DQxPt%X)A{nnyx9MsC7>D%rNU{=wf`7Onz~(7bU;;78EBt( z^AXVDkOIDzUuwWZZUP?N5@4r;7Z!T7-Y%VhXw-o&Zz$zQD>q)mXrZpJ0(W0Rf&*L) zzxnjq_60FAAbY@p!Mj`LyZ3Px(5X@27}G|Ub^%R6D}&=tBo`8YI$EIkd+gi#r1S}B zk)dbfKL!>ChSKej$czCsI1VGlCwTF!;Q^msR-qt92E&t{-60=dc(h(Bbp^3LfLJ;p z)(;R%!?RliETjs$L&>8zj0L=c5MqT#=e44Phdr84GQQ^YXg4;x8uX{`>C;88kXj5)Qs^bVtLq84PJ2{CNk`_=|sVr196YsDKiq0Bn6s^HIje zzt5N%7|NB?`14s*()jCLR0IsafyO`+G$3(cp$qB)m0kh2(h+uoJG6W*ogX~FSsBvu z^U**}5221>!4Nk!*fW&GdvyMO@lyv z3m=1{e>eH~`)^8;eAa@Ek|6}BDnaRMw0C6yAZ>MAD zCs)G*jypjv^XO&$ah!p{(eeX-Gf2+0x4_=9@gGRamEZZJD`=7fX>m8Wyy4#_!{2hd zM5E!iUWu}g<;UWJju*aqh6qG5e%RLsxw7Jz@t+{1yt*ExbSa# z;rRXTYhF-Q#PK>A+JEVFW3s%!-wf&ixOD#D-zLM(zwLGB`xnJp;N;}rd4uJijg?2Y zh)Ve}(1E)<8A1I}1_l<;wRd2niX~h+KR6y^VTp0eOTsnV%B)eGHK`md4-z=y;4pMGu^cK<;LE?Pz!a!mv2TtOvKM^Fs51|6uVF1rYlmh^<#50%HG# zusNC!{c$|TBBBR6Rjc`k1X7a|;@@CH)b*F0hdU4OZ{uO>W#Mi_+w`}Bj^B;7eep<|9`Re*Z=>Z z91CwhH2wn}_`MTE@H?GrJz1h{cnOg}($Kk zp$yE)h6i4xy!-#(qnB0uC<8+za(xCW=@CUPsF?TY1r3b#mZ)&N$XELJA9Qz(agB<= zi%;q(eOAzDBNy1UpyK)Pi_J=)3WOoxMU>jV|B;7b<$oyJ`DHJ_lO*3AJO8zSPKDBJ zRRA5crP&Lb?Bds)>Hy}=4FFS16TsBk0x-3;0Zi?k06y8PR{~763V^z$oxL1jYAVCO z|NoJE<9flTcjCD~W(YqJ4f!?Fncf&qVq=9B; zB-BBzP+QPpp!L43xB2@(OQU^~e;eLTo8V*lo4*-ySf7cuN8>S&W}nVq9-6mYI={cL zQw2u{WW7DV=LrwS(;lq{Do=TI9)8&lYP)qV1szk?e3;Rv^Py+w6OUeJevjq{A3QaW z_;fz;==6oV65=-B4L+UEJv#4!T-Broa#ibrIxDbKpyj`hlz!z~v}xIv9EU(0tj>t2^N|7NSv>UCx`Jn3TjgMZG!UKY<@ z9_HqQzdVvpf{LW$EL;kDdV0Mw%*}`Yy>%(IQPCs$utztK$}twu4o*;4 zGsZC%)EbP%-M-g=wA4FmR1`n~bqzFsa@r&L7^uxF0q*~QbL~xI1Wj>+w!Ax9-r?^B z4-A98&ksO*zi}WG|e;l7^w3jaEyg9#fU!|WaxHf0NrNZ!VJ0`{jy`{ zw{G@M7ZsTo(ZB!y2TlL*b>}jG4k!Qi9aJ#-sCeuH<#(_f9l;kDcfNm-4@w$8{+IZ7 z=BNmO+q(m?0C9eiQ$aqy=S|Mn0SzO;jfI2aFtMy(Zml3%8E zhNy_~Z$AV|0UEA`-$1txzRvdOX1(G9N><>F!6#i9LH7Xv|L+0b7aYUF!0>`q4jdqS za>|No^a3j+iH`WO`j!~ZYL<^KJ5(R{#o!?E-D%U_^tc2SY(Y*7heVPMz=US5CF5q#-dcaDkxX!l%+iUVlT z9!U9%XNsV$l0W|Qx4Z#u1M93&kud!KqCpn4VdID^@(5sOj*11y%{HK6FxF?zj10{d z3?=d}Od!eTZVIUDV(8I%{KX@AP-5n9eF9!U@xn#(N9G04xn#bU??I!a4E(+qzHMtuo4*-U&o%!P0UOd(2Wnf_s6;ZBmU|q0#MJ!A#-q1N#>4VoQ9A#&U;Nuj zY$7}kK4WTrW&>_j2w0w~_w(olA0!#jV98u6(;X;bIRzYNr2-z^A%+|t)*&hm{4EiT z3=9pH+@%Ug3Pc?Ew_Wq-_7UU&U9jT7-vZhlWBC8YNoi2D9rx9I?rZs;zsKv}|NjnK z8M|XtI6&({B^+Ab^0ziHGBCUZ-RI}g&HB<2p49I-GBSWBgkfnt2z0Twq%=6KD@lXX zy5U>!Tr#NGGTpa_f#Jofr~m(h&C~&zX${ixTneQ0XtMJOjuq z!?zACZ%ahLO@GUK{5?N@{|8;0{{R1LW|z*tNR@w!3aG#=73qB8(Rc(@m?9P2FCIyt zt{({S1l9RhAcM7_ZNs9z-Fl$?SotoUFPa~)gQgEyUnjX5zV+-af#yVgTmP4+g0w35Fn;#we2b`qCU|t-cl>|dhw(A!c>B_iApM;Z zph*)J6_5oyo}k+3D5Gzu9=I0r0gn!FAh{dV8b|6sBeh?AL0j?8d4kp!fA;MB1M-5D zXE%$AN3RFFNAtrU9<2vo@RBst3R8M-R(mrCdIpPd!@S zmWI3z^Wyr{QkgTCkr-=&4qcXnTAjY>6VIRiFaR0sb1f4bX z(WmqIi7@<3YSMO3zr8gXh6tAmEjNQlH3qJf!C~t2V4!Gcs3vB_iTRt!@JkS#>4VhnW0DH zF|f+UXCS2>&F8?Ulk64;%0#dFZ{qCe!!7mkj?PGYktp8HjqBgPBvIK1j~E? zXgn7bP~i0qC4v(`=2>8x2N~^)J3Ilkzta51!n5;R=kFI=CBdzhusG1FZ$^(^-Vit| z3eJjvv!Lo;@PW#-US9t=aAW5;6Ms)R=om%t0D(`hYVQsPh8^H}L}u1zQ;^ZF7hZaS z4$*Hu&gf`)pTBn=FQ~8S(Or81wApnhXs6Dr7ZKwB{=ZaYWnkDB#lXO@3$&ufqt_O+ zPtT*<_reQHv48(xGJzJx^|HoVGBO-nAWLj`8(1&;S6~ z^*?w(Ev4h0-7LpEdU-EefPCl(y6y9kH6z0@2F915whn0B+BFey`+%9h^$`!~n8|&v zOuZf#ntyWfH(%soV3^T-;DSdl?*t1*hO|yc7mr@ng&+eQ7#UwmL0nP_wmb&pnKrN# z3*$>r;(k#BF`0$Gbt%X(%}1HQo0Cn0cQP=%s22rS3eFHAXOK`1Lcoky>!F-Ry9 zBIFHGr3?~s6aDvpCun9BbhPoy3?2rCouG01*E$}(tgp;Lff555&D+TfT5m7v(aZYY z3M9ppWvhRzP~rp#W}vtSkMj4$1o85nkfOo!O$upP7r#rMRE z3!$T#sJUhall=P+@LxUmoOU z4Ukv%Lxs=&0d?^~!XhBy6;R=AaN#PD@IgF~xUd#TSPm+D0WK^D5@v@A&w~qdfrLK@K|J3M7yb$I{7tCv47l(s zknka>aNnQ*|962J zG=qd=puz~tOF+V|P+=Z;grtLn^^k=3*@CyPyng|5KX|&j1GJdK2I~*P z%bLCH$Ysq`usTMVY|mGaF}AlLNTdRs6u7=GKupuoWJLSNwDf1h4c z4HHHNpI+TYGZ5vv0DS5RcoWB~7k~31QS=tHF29#m${aKaz{J4cD#;1DBd88^>61Hz zS-?=r?a|8$N~oTlZUvwN&|*D$d7qkr3Nc2 zOK&(e*jO`^UVPCb^6$T6=h4pNFD8kAn(fEDxD9w@=E3PRdw znx5TapplvT9=)d5;u#oTyb<{K|Ft@zJ~uqzX?dh*#%l$bD$T8+mHe+>90rG-u8%1r z!^?2+$@sPpj2Ial8|tGO9QT2GO(o8r-Sq;X>fP#vC^-9<2)vNt2Q~hi|1;ei)_ghB2<2JXs#;s9Mw;L709>%rx5{1C{@37|$HcelZ7PUv`; z;WzMUAC{krg+NDlc=UFHf~xe!i(=4lE#GT!s%m)!I`65WK8~T3@5L z{i5dwxT0SQ=6Uq8&M;lyY~2hAa46W<&JGGn5E=_SU<-sCo)9a~dc~9Cuv- zsg7$mya*Kj_aAomLT~AU{h;J-cdHsbJAE?0v8n0cl1=^c1Jn&-6 zjsO4m+k+N(X@Noj6pH*U8KBM&hz~k7h?kXt;f1a+c$q4H3llp7!wY_}(fqCItf0hL zFTha30UDK*;0O81)(;fXJYXdyN-q}kf^A>J3$xvDD`JGf>;@=IY#p~UFc^L_Jn*7S z2x<*zOKaz0!*89(UtALU_rLT0i`^jS_u7hWg)XKuJn+H;ruryo>)&B;FTC^ki-kh} z{_g`7RGs%oc>ewO*qNf2lA2VS&fwAM!RoP7zaS;AI7KhTqm#p<*VG7nQRV#?3qUG+ zc^i#EWd`V;zEaf}Yk0uk-U^cH1kC}i1qC{2bOV&2Z@sX&4ysDNycXL5S`h8Q%=%gz zG!nsU12F{R*r^A6dR4WI85umAZB!Wep)+GXy}E41pbM!wT|u`})w?iM$b)7Qw}3l@ z%^-U~mlqfwcyayO|NoBs+g-u?ur)k-O$)a`*K+IL{Qn<(v_3x)JDAbVMpK_bUNcNg`t<{L6H zq(RuSv!O<<7C4r$4&s0sC?J_%0-yney7Ai!20?IP*%}NQ(b_D{z|eWvv7tVmp%jvF93Me4PCBHidIsb;&|nYy zi{%Ck44j8w9AN$bzx8&h6g;RvLDIb$8dOX-{(}nZ%R!*{iCqBA|301HK@OQJ07|ed zB_CQ3lqz?JE@-_};s7_s%%j(|3TDji>;M11R_Q$cVi6ZCCwOm0IPB@Nj)zZum1nv?Yf}(2Y0#H&Iue04E&1^7K0Wx+JI`e#aBQv z$m*ic$k5Hv{PTYad$+}lI&M&Sv0gNQ#u}eTFRNM*cx_%XSW0$@A!rE08dPc;zPQf^ z3eXY_k8bG~k2%2!`3)z?)u4pTg>VXJCcF3wJSCYscDYBl94OKE?6y&P@rx5=NTt$? zg%A@~flUCFKMyt`N&u59;PZ`R`Tzaj4ao_z4>dp`ZM*6O1H(Q}W?09g^X7{aAQO6R z7oKKd*k{Lzka#V-6QY%O8ANM7iq;nsIRE_zcZFWC@&Eh3AJP%BtpckxZCVZL`S^6c zdXdQmQwH+?PKYwzV6ZaV-qj2Y`%FNM?A4$;q7`(s)QjUB;IO&C0SX(C&nnj=Vl?XV zf2ge!`2PLxJOtME9#pbUfM}Zu*EV-O1H(&D@%5qMGT6Yt@NzLD0|U(2ht)x$ zWBPD41H(&~|NsBP+_@SoZfmuMfnlFE0|UcL1|-Qoh@=ll(hMXCT1pAiSqPEL1WAHw zf|tiY8^WO84}nO|07*K7bgumOA0C9}U`bP*wV)vM=ng#qTC1Z35A7Meh|uPND3x2w z!0@sc$^0*BAn({Jg2X#PGgsZN3qS{j_1bE#1wm%FcT)f@YSSvGLYUxSOUU5kkMgp074 zZ{zv*-|)6aFRQHG3~fBhdzT( z=f4+cS^oV$=D@;moI$T3GsUCVl+_>98NUDG2n!_jwA=ti8|WZRQ~!0K)ja<_dPC2E z>hk+9K5&8Bjz>V16f5h$|BlUn7=1e5@V9~%&x3}x62Q)S0&$i+7H9GE3$XrK%fP@d z$ifI(zHJY(wAZxJ58?&~5V!R}NdjnQwb%6gTA2MOAodr4?LP*xzm)}K|35~b&L^cN z-L+?|YfqHw7=C*Z%LPgxB~Hyh{+GJG;Aa7!?b~d@?8ra$_-k&T&NrY-5t)5DpCFCe zx?U)i2d%_KowIcWm!z++gEU-vp~?)JjduZ^7aImz&1HbTnoHwF67#?R%?Exr|6nW+ z_2@NKSlIQDB<^dFnV^&Z8ck&%yhxKvlIDI3wJDaft~u zy75Dxi1nC@3O@sA5$B_Q!kyqk;r$C2Pyluw>wN#h3&d*v_rJuSf4_^0KLOKtFcXEN& z#c_1zs7UM+fcWY@Xj(rTs!D#JD3WL>RFt#ZMMcKZnythaypRqwMOn1R@a=17k8ak3 zpc)Y3)oozr53X{h7pED)9=*Z{Dk(r7eGAIk&}fM}^Z&o$+t)l`_jKNW@s9~@O@~1# zFL;BH1#G9q9R_fQhtDU1#-D>dJO3Hp_UW~KFT;qk{L%%qzU;SAxtM3S$$d}`lLG~J z^I^u;1EtSivx4{Xb>09QeHhwaLyXUZ&d*{%o6iN^Fb7)Kr)_v3#iN@?RRfgSts&P` z^S9mxZ8dE?0%~I*O#p!A;1fXWYBTt^g)?^hv$%A-bMT++Jl6<1%e%pm|Ab5D0no9C z4c+-X{AYKARxoP5cIn;QkeU9!@h^z$!teCkqgVIhYS79b-50T-^P-&ILUt6ip5&i- z(1qXQTj$a5cR?q5dA!&W4%%(I2Xy_G3%}L}4!&Y({>fA%-s||!qnFq5Bxpugq|u|-bk=GHhSx$Ky{x(r zu?~-3)6UhP_WT|d&^i*s11}~sfUZ(#_&uM${o%j=|2uCsKZMwF`QQKl2VWx_{Ne=2 zU>T6Xjxd9-K*T@>8-NY2QK>UL(0S{HHP~FxUSkQzh9C3!+nYg$aW_BU2kpj+aBTj; z$=?oIkmH+t&jWOilZy(6XXfe4zDWX}nXijCJ$LoUyaD3*WS#)?K$if%Q1Iw{=W+0z z0%*kH;Clrh&{2G^6g&>TwD&mp*52dbYkSXwFYOQhU_1Da@8Ey7gTMF~PcXh?yzjzz z$|L!PPx3XNf3tDa`~=z-!T~zw z#lR!;9Y_Pnh=adH7*9K9o&#ysJePgS#!>TJ_5mA5&;@2MJirSF{+ciz1qH5xWA=d* zN6q7)!&Wp54|Msc2y~q2Woc+W;0Ica0ZJ{P1t1a4Kbb)1w*-LFI0tB>oQF?m3FsIg z7Zm|mTKakn6y@Nw_dn=9Tc6$#m0F+PIVzwt zT|9b4udZN#98n57l&rv~^E)U_Sb#R}XZUpf^Xc|cap=717~~iXI#da?NhSDyiHZjZ zgYK#X(f_xofKDR(zeNRBOJ>bLd z@zJOA!%MzD|Np;8`1AKaQu)yQrUIqFE%4~IJ*mvd@M6dBzo4~kJCzw3AQMX-y{u0_ zqkG_EJ9=wW5?&l-{P+L3iwbCH4Scb50Jyd^y!2uZ3dSoX4R#VU_ z7#|f5|6Ub6&*mc>pn1oO9H2Rb5*ug!?JlNV%@6*8?p-P^^=_^)m0;lS11(|j?p|XG zYAt!RzU6Om1Z@iUWoSLg-})0Y6>rE0>WRPD_51IC@9rL`x|94Z;Nx$)1wogHf{s(@ zyynqc_rLQ~^CM=D=Ksw6&GW!>?V$Y(t)OjeFG0I*zypLXo&R6F;b34uu@!U?u1B{k z1IX*3`(#0P_W24JUV?^KgCzrhYZj;n0b2Opd{Dr{@=(pIm!SI}4gbH81G^2$54E5Q zq#NXky3)?`oxhqNF?uxrXXI}Mty(txW_a@DV~~p8ObZXqYli>RCU|tZUhwF2ec;jQ z`oW{qmB9maglzLs2M^1CrH?_EGV6I9e9q#*_!D$jr^mrZET{(>GkA2e7ZK#M02K4bCd718r(KE%;bV=BNd$nd(#qnCBJ9Mo)aXb^vgn$30e zKg?w%tS@q)MhbX<&)F^!2RH4SEf^pPYA+K5!^>@;lRa`&I9{#?Em;DM0hxXO3n~sd zK*b4b& zA^y%g^8f$qxgN=94WYZ}LF)rRo6k${{r?Z11Ns5Vs-T7bIVuX!5CYX5j(b=^`L3Hs z1=%?+3=B@)E{e=foh*wT`M0?;ICZ!z{^QxaxAOo0{|ptvh6j)@69BJYH2nWU6C5Nw zDqfu~i@$g_A7KG6CD{hr#w6g`dBy{@(8lx|C^f}0fXYEl5F4}t^B4o8N3U#`3@EO1 zR3t!0HcSAeNC}VD1NCAa%|HH?2y~u(t%Go4+5~WLL-TN{5G3!G+(M}E?rwpWj0Z~0 zJi0krPuA)CbiVUt{OZ|!RKh3up+~O=hv6l}dWU99hEjd-97gj&0UygpHE%t7S?|d( zg4V(Dx4s3P@dlds0F{-X`v_iw&t&N3-2+kqDhom7Wv7mcS1*s5NAp20&_YCx-JpS6 z!?&+_(M{Nysl&eEIty9F88nvOLnrU5c~`u%)LO{H=;V z!DY`SX>eGS@_u9eB+bac$lrS62UudKG_3vy)mCMo+6kKPmVf^H-=~|~7gU0Q&JOqN zW&~9ypd!k#^T194KG43f5ETc*{}Vhip>u0%7(vpYH8}|$-JuMvm+B%qFMIZyFoNzu z@@PKF0oh*wk^@x(>K@%}9<2xJG}D^vKCzavHPi_(l*o86UhvVp;9+^fgWvDOYhKWP zE>L^;hrNg5L67F60v^qW|CjFZXg<#9V|m=8SC&tTk%1q4hm}X`$$DAClgLM#g3~v` zv7U^l;H3wo)!Ey@ka|`Y6yp{ijXxPq&9bf$@nB_`ZS)9Sx_CApVuUKRm6Qez@0oT< zfv6A_gBMdif!a{Ku2P^<$C81+r5L;}R`kaLP$PlA#pL^cQ1<6fLNW39=*15Qj82QY{AyB-jf7bV*#1u*s=iB zjxap%!r;Jvq=6%A7nOoiLGV7`3Z(Ac3$IUqk#>xG^s*k51jReZ>ci0UCmch98|oDp zO4L0%e>d3P05wQTw82$hujy$?M$qx(mq5cD?|=OL-+G{w6`c4wUw~`@?=th~WmORa z`Jke}@GW=(tn@obWyNjB&f`A4rtOl947=n(tGkbbhJ`G?{{8RSc?6Ut*M9!{-?#Ni z#U|%tEG7*5K*OP+e(<%=e<8(R`Tzg_k1;U3%m>{j4m#fi(Y^)k%;S>)*{b1@eB37! z)N5n#>Tc16q{9RJEpbes(^eHg$85I*fezyP?AL3e3(DJ`jPF{%Rq*#ZGdfz{apdkbQopC=oznLnnzz8s!lnk0;hGmgZ73hc zLmthCEI{>?nh!a6G#~s^`ut@8D1-lhVGVPf14w^2j|ynl zc=BhMgCY|r3DYhm!J&`9^FD7$)6GS34p?}*Yt-NBSQyMvmHYT z8))ttv>31oWTSv%^O66hPhTgxSU#?e@koB{oBYJ1^R>sp$1I?79N}0{`O?jz;?u2R z`2U3x!Zq(+r1CK^ICi+mf->hsF-QR_e0^s z|3NoTf)-rzih>LP6$`Apg+N6?g$q-Sut%@y2apEPoTK4^7aMn?6$c!p9N;lIf#}08 zT;Bct4@w`1{NvI5CZh9l=U32XI|&pQ2YB?_E)Zp8c+vm%?|;yZ0HDJOTS3GBJLZ6T zVZ7@=6zCk3P9GJE&R0IYtgRxTQUbJ51GG}H^B!nWNdRP@C%Bo#-{JvEGob36zt0xT z1f81PnFBgdOJ|j1=Sjo=pq{w~@@2^2@*AW-^E@cccy!Oo|)wC|p3{11fhBDR2V#_BYV^N*=wo z4#JEKo}E8HLj@Y3ZG#4oitapr>o-u3qdNq&IUN*apd@H{mA`fVKk#;P9tQAvFr^}( z^I+b9>LuHU!i)?r7~cFvtserw`^AwDGkT%(<}YYN9(=qKC4Fc(c3uOGng}=|FN20` zdb6zqS&{o1WF2dp5U34k0UnqDt>I;M?B;iEeOti;I{eGU@+H6j3DEN6w}x*!eNCyV1zl9Z)ut1x?j=s4122`}X0ImCY&%nR{Rqfloh8>hVz+(iL zK;q!8Eq}`~(12va9(GXYgVCeAM1`aCJ9wQazyB}Lp-6uJKo@|58iJiQDjc4@GVC6W zhe0NFo&}YW9?eHTfVL~OeyfyzZHw$+2A^(iP;zr^{Z^sk-ce#JVEF&Vj@N(xdw06n za`;&OExq?z3!x!xf_HO`EeAu1p!abXTTtQ!?YRMWJ3wkTfm)oPwh0F$fO>U#1;K&# znxpdvSbPFF_&|%ETrHpSx1NI}5Do^=D8ffB3E_b)cTf)!H1GZ2N8?biSQU%ME-eeH0Jb&4P( zs6$};R*;e5#gvyIy{|PvEn^9fPSAazjC`Q11S`4BK&C)TuJ2nwmfGroGOu(bp_O1XU!;3R7{{BD4$_hI2(5Kh*9zP?4C%EAW*;xbb zVDR@n1nolZJa2g0@GbJ_5GcKYRA-(BB{h%c8V-s7{H?b@drJ0jfRjqSAb23A^Zttz z&>@ZB@s{oua4`jLBZ0ecD?tSmr04?;<#e9%=*4o^hx^M$khms|mzCu0w(B|msHAK>sf_>cv8H!a9N9>-Zg zeM|ixOF5F$5GjXw^Z);^vyZXH@k3J0 zc78^N7je(Qx#E6Bq|1fi2)hPGDE3 zGBE6iCa`InKrXXA&Bw^_;?*;>1SU|*?E&5j4Q|W6ka~{XUJmx?m7T%|>et_Y(eUi= z|7O;kd>{vR-j9B{;Mw2*pt`aIWNh*?(2&YrK1K%6kcvmI=@kP|T`78GDtH(L>>AL0 zl{SPdr+@!9+s*)uB-Z8o^qSt*10{Jok6zZ3dY~vYcyaS3 zD3D8oe0pu$ctPv0n(ZMSi~On3kiEY7|9{VJJJ3oFh8MdaF{$jxznvcx8&aLeUn~PT ztrOH@$%E-xycrb4pn~Jui}a^||AV%JGJ_6Q>I4<IJB#2Q}IrKvJh}=kXUyK|TZrfa_#Ln%3X&|Nm<H8{Y@DvUof@ zzoOO74j#R>54af_UYvjY_rKvu$9)S|u41+K0^^HG^UKxLa}cZ)r! zIs>f^W$Nbe>=m*1XgmZK=?qbkaI`#E>g?FC#~xDXS%9j|F&8+A+Ch@&nMu$jYO)bD7-9`cqT(<9g4PG}f!1hSGchovwVrhC z{O8;GyacpR?dM}y`EwAa``ZRkfU~x9fyx*TP^uR4=r!F9mF|4?;wo4{jf#Qcw-+-X zfrd6g=MO=K9&ki~4v_Qd{NdaA-ltc^!n5%RXaLcr^Q)`jx8p7<;MxFD z34rTspKgto+a)5--7e-F&Ydn>MI8CJv8XtAxNH?@Jy{{++~Kno)Q8s}N3SX8M1(}&n*aZIgLj^U9%FsN0V>FQZEZOj8D2bj05bKp9VFePfhKKU z{r&&{<@dj!bOwri&}2q0s|_o1CN2b73eChuYd~(aP3K@_c+vI%EfYI{PYq~3;sF{n zf%X<(Y)qroR|Gd%?6PzsoW*5yIITF;LV6$4RAA}win!t=<9{9JKK2(G%I-E#h#V_|Gx;| z3Tj3;vO${>7HrUFgfYl;se7Pi1nX>Y!nI&1u|Q5EcXxr($RXF(Cmy}53KKw9@rr>c zQ18Ci^j$wB;k-Dy8f|PrfWK7%G`!aN0Md4ffgSen;ua`>9)GbJT+03DZ}|sW#c2BY z@Bhx@FZRI%UxEs~-g=K0Ia?tq0d%W^N9VKspjPdREOkbMm)>PkjUTk#kfy%FlXs`-5u4!@VHi znH~XCqD~Ee?|f!>sq_5{mnVP!TfQg-4Htlxiq!MBfC|>$ zV9<`a02WZS!SL-+9e)d`F`fphcR;1QXLq=RN3ZF$UXa<)_+Rz^|4U)ef|G+Dy{uvo zo#6fOpyobUC#Zc7)hUNaIgXt#UigDFU;F$2{|nQ*u*CJK2buy554_mF9JW~T>7&2@ z`M16I=w;o^3{99~P&4oLK;p3T<%|C-!MUXMtt0=GBQI+~xBa#rC<*rLj(2!*_zo!i zUYxlD>Ntb$!?r~D!lUz6=gSudSN{M1(gUhV4r==C9=KmSZ@!oZQlk17Y|Beoki*a1 z{`>z$I>hXPJ1|c??`B|lY4#uHdi6(t|G#j8DDs9YdI(Y^_#b>=5Mq1+dHrMn(y$fi z>>Aa1|3N!mVpPBzA2!|wdF6Pog8*d3BIM+l&aK4nIRPG(S<(E@5nIW`~XPjfu(%D~9r*j=NN@%?(^^Z%f%QT5<2 zs4(cZ_UU{Fni96U3rW(ZFATtzys!kh3*6S^0p)(^(zL@%|Nrl{evt#VjCDUFc&7qr zR`K}@jR)WyzZxV4+8~ewa{6VEDZRWcOrX9D<7>Yck8Xit15`cOf|3-(Sf9>MFEW?@ z|NmO)#im;zFTU6fRtBnJ0}#qyxGn`%cB~#CD?o)aA|k9^FO>45pO16w=3nUf!&utm z9=)dD6hZAL(R@%}vlGcs_Q?T&^&;lILvB*2n&qvt+UQA%?{ugvaTRcOlkOye> z@Qw|jGOLvJ#r^f5Efu;;6hU!X%dihLC;LL{4mke!Tk;e@MfMUE2k@B~H7X7sy}F{{ zjU{tbHZU|l8Kl@;YIKo=E**ZPRI7^u7ht%~lr1q!`<7Zrmv{`wZMUeJZ0_h0zm z08dLhfZCCc-L(upozFl^BzN3~x&U;;hhyh4&z-mm5yFo>_Xh8#r>QVW~#K7?4@)l4eRA_(#=sD#4H5m~5_GQolr3wKMd%rD+ z&EL8G-~az$H$kr2esTReIB7q)4$1+rG2G`}3=A*sF8TleWe4csEO|(O^u=nh+UEZ( z{4EnfH$Sz@F(9qsoB%c!9<~Si85my7Tml*b{_fFhdZwR&;k61xiSWT!9MBlh2B|8B ztBL?|kASYE=7+fxZs3d_(EZO#kRq!$M#aOU^WKZkH$f}HKrt2}1-{`SMa2Uitey8> zT!zbANHKuVv++m+7e<;=3=EKnz0vp^v_O`>T?{1Tc<=~^;akhQ{LNWB3=EwwJbF!7 zKnHe#rtNNNz5<13J;=<~1DywVfqInuey14Ef~NXUzq~35%7wKIh*W0W%fRrWe=(B7 zJMX=)2k8f=GBaMVoA-bdbDCrG|Ns0gcQ`=B*AJ-MJbJ4cU(CA-od;y&Wnf5i1>LxC z5H!vyz~3^98+5h9gwA&!jK?~m?z?{jqP*`I4+8_^QOmoa-i$!e?PDwwVBP#JCwM`( zPaW}OyyMw<6f*WKft=wO9J^c@(sl|l*l3rC+30%oLb7V980Z4{w4K5XuX#W$n7C)J zE2EF)gEY`p@Wld-4YdqT95LBhKTs(T|isFKy(FSeK+U}yFkk;v<%>{OR=U-pT=b#nZ z4*X3g{(+0ZxuAt0y^!+GqgPk!Hz>D4!Vm1qmx(;+sl=g^f#F5wLZnpEI|qE2KPZI@ zg0d$#wbY5jQVVG3K4^tJs9fm0_u}0(SSE-T2PN_rux3a)_Y}uS=Vl-w$AiZ?Ky67# z{syIY(3Nk9^bWee4w|0&LB?ZC?{CG>eKMhgf#Jpc1;{?x0(Q&`FOVT%Z)AZ=%k68x z!fDNa|MRy*aDj3fa*AJb36g|cY`MTGHLdk+iMHWec>3vl=fn66mfqh(vqv8X7bLwG zJ#ak6B7<7Ig7(B711SW%fm0AYoW2WSgwqoNP~HNkKT!B+a)Vq6D-S>u>Co`W2N{bk zd=`Vu1g(;R7i9N9;Zr{!Eqp-jA1#m}V7FO;ulQX9t`Q*NqXD|{^aXPG^jw674=-r{ zdgpO$;qwm`KIcJ+9kg=g1{+fN$bj$7A>Nj3=DW`s&0^( z%|DntdUZX1fa=3G76yiwm-x^V#O)Rah8KPFkUZRZ?}Z&yGkGU6dc?M8=fcy9UWr&7884eWx!s`W4?gFi8>%8}3Ge|8s z*b~@5Ndn>&xJpoa7_Q|UeEv=*u}dzLFocHcHW%_)u@mn8eFc-0qJag zTcTq4w)33_bBT%oEN+ZJri0=ppBL)Zd*IRO~uNE-vg3-h^1!3t_Z zfYSIQP|g8+C`1_KA>^{aNf;xI>kEU@_ykb;76Wa&hSzt<`5$B?w)Fi;2tB%=w1V>g z95i=<(zh4L5U{(VKu6GlLXZ)p8l_2;1pN++Rt3ZZ;-Nyt=2w>eH_kjjGI#Jxm19RV1R#5qb zKmYlIj0VN=0#;}ogUTm)hL>$%8<5?X1#(|Jy8A%!$^|l{`3F;JBtNKG3cB3^+>QZl zgM_9%NIRz7!tgCPJ}p3Zg~H<#sl3_`$xs;W7f@}|Yx*CQV{Jr1wU%z~7Z3%hyuh{G z%i=GvR$X)}1H()2Z}28%UsOS9OqgR*V3ussfUKoMuzNg?LzWG}~V^w$xqko|M28z3uKycjx-XYK5;=;wi z@Z!@Mu#0}5L3Yt5P%!_P`Tze*1umHNji12-1h8wF{=sseXXniqyiVXk5wgUtdkeU@ z@&KJt${JL}z~BMCfa&`Sb`j9>2S{V?1=pFs|6d;B1hrtmO*L0TNW&l0WW=nQY+ycU z^=1S$|GxWl{__B>ba-({8f3Lkudb^IBSVEZe``4C{@&LA9=)QS&7jy}odKfwT`qWZ z-t_2YEz|@%=mdCZ*@NHp0(eiVN3W?xGXukmB`ZMP1^!MUSjyvXVF%^TGpE6!boDfH zD0w$CFn|}fA8U~TcUa)XBB%fbcT(@YC<5gKP}sLV;RBU7ka``wB@mRJI`6#*fT&>M zZ#~4vz|ak8*?>>Y_2}ILuH`^?-GJI!FP<)jw%kCcRCsnC_2|`o^$}D`Amg8f+A0s`y&Ix%h~J<3?LgpX#*Z| zupW*eC@(etVBv2q<7Hsz&Oz&qTSh=V}ARD?;OQAkkCgR9P4FDw>;Bg>;# z*X;wS1rP#CQ;wj)pq3I=2GCg+|3NG9dj&y@Q#Z0gHor3S{KJXy*Lia1ouX&KyV1Ak7U73@=Vj`TzelKO}pt1O=xA zKghc^DiSa1KvySpgJMYJ_}~98{of%n5F$B43I>nPcQ5QA_OkG|HnGAB1bErf`R;`r zL;(|jYa%PwF3-Cczfb-J9em5g-)afE%oEg{0IhKc1u`gOf=Ux`sRAn8`sYJKlLOqf z{_fFv>_s#m1H;R|T=3%A@RCpGe^8b43GDNO9^H_!p9TvCk6zu>x1i%+%OS;#N3Usj z6$8VIisPVZ<<e_-< zgtSayW?80p-eG z-AM4d2uMzarS6BX85mynfG>Fk6{0t@7#Lnmg(%m;qI}b9L}qvy57)K`q%97j?K2N( z^#!Ue)yUf9z}g;y){%KM9tEus0Ik{Q>1F~QW+m{FjTw}Dz=vr42CV|hy=~cuUwz z#~>NUr&sqgH^~1rpo4tpyh224tJ$~z|6g{!f<#KM>C_4ahL;&!urjv|;4EbT2O-X!0o; zIvEPj_`M}69xs%k&f{VX%DhhUXNZ5ac@3n$RPYp-tcOGNSkRSDLI{z4n9g)c!(-oM7>p!sK&E(QkB z;b|5B^!b~zXMIQf@L$(ae3=H67rukhi zys)_nYUla%LJ~KB%PM%|<5?*K!;4r>28NfHnL$?8s5n59Arr_ME};Fdy}G3@kbVFB zh3!F*?;(c0EO-I)$k|c`hL@m=5}JShhuP)Pt84xOR8n++Hur!=1bskf32W^~D@a$a!zP!`$Ht6(QIY-b6A3NMvGIzi)hkUR&vWa*LN0T0W={LLI7 zDbO;B*8e4UK$-0GK~N?uz3}oTcx|vjcMqg--gyXA&1!V|Y+&*1jb-%d1Qnf3oQw>w zIiO3|8Tt3sGBp3!DS7YE@}Iw@8`Q~aJp?xGu4m_6{(aW1|4U>X8vg5)@V>}7^!Go= zoYn&+j~$P>g3d`h%IMMg9#M_od(klm6z2RbO>7JdkdaC7WYS4c(Q%w_9-Z(75_#QUM*?A|NnpS zcOTf%Y$%T23rgV3AV)ia4)Z^Fl*8fMQAYk&e^568G#tu*y7LzQ_Oq`1+mC~mDM3TW zr?&?@RQbZ=8>lD7-!ccJ9&`r)15!wW%C7Q*kYM!a)irs_z)+$Attd<6U*td>RJsp2 z6m>ujD(Z&yJG%dX_Db=1^qM+>I+eYw-;xn+%hjOvb9ar3#*2-6|Nej3@C%j)S&A4K zUi7kp5)mYSdi3gUdxA&=FGJx-+G=2 z-E&=VInYXSP~ihDm_h4tK@A`N?eK||v!F#8;5n2Tpf(A73<9ZOwgwr8tze!A8rv2C zby`46?ocZSHjpOJ7)U-p?(0EDbA>_#qF31H$uRt5uK|us5SruFj-+J`csCc{%^Z*S|fXoIDP;5*@ zB%(BM>A44zoHKWW%5+e#hWi&N3BprP#UloWmwDj2tVYGdvlE;lKzSV8js%q#e8<5V z7ksr?w~LAZ{|V5Dj!$QfiUxR3!6xwNRE>%P185V6L`jef|Mnae1xNnvM?kwKKp3ps@!Z#;47P8C@)2gH0?t4JrdQ zUM$`9_y6lEpU(F$XETD@lJ8$k-UafNt^{a}Q?KsYhlq%QMB5xtb^+~(gVk5yD1ABS zA^2#;FQ6e8&@H{-{zE4Vs=L5ezu0&J;;K|oBI&MC@o2DTER|`fXVfYc^JqTKv=5Yc zU%dVgQ3V>#0ekAj%sb#oBo#N*oaOeyVJ{@tmFRi&vi^Pm8bkq~@F@SnY!|o~v)_eW zYJ3Cvz@{Bk?$@Yzyx4RW>|u2#P+JZXhcEp{E?*Lxv~7Jz&OI(p&d zkAMIFzmVSv4j+$R)7C-;hSy>*{_OyV8#`RQG8?h@abh#bNY*L$LGv4+m3E+w-8!Hk z?qyvIT9?ob+JL$n#Oh>3TF}v=0$K)9Dq?uRqwxr6i3IW*#y8us?H>vOFZ1y1{NmV& zdgv2}Pp_>Y$N`R^MdzR$Y)OUS(Z>TmosV9`w}KW4xTvs!_K}pRu)zw!7h=01DW%i` zv;qCbc96G^8iMY_g%o0CZlEijKtmC4K>lex0N#n@fw)oXMjF^c`4@M#gG1;k*a*<7 zmiT;#{VyK1V5}G61uwjI1Yc?yd-w&z_P?M3E(QjMNQY~J|Ni^%pkf1lRQ?aafB!)k zq)wmr-~Z`6|Nf`&{QHm02FZgkNE}2XV=g|3{?mg0{@>&M_kRhL4^oo~HCt2wVx~`i za<)QRVrEWiinT&=erZmMLSBA}LP~0JNl|{KLULkqMyf)7ov1ut7F(&NSShF$Gi2pu zLJpGEFD^>f2V0?+%#fIp5}%x!R}x=TRGeB8pHvxNQdy8{rBGC)0AeVVCFYc-D&&`z zDCDOp6eZ@Rry^@1UR`ERPHK8$4w|`X`9)yQWagzaKpdwBb{InugEKr-@(WV)6jCya zv%yMJ71AW=U!;1K4(jvecsD%=|os%#_r;lFYQs)FK9FaFjr; zR>(}x%Lmz)l$xAaTAT_Bn@q4ti6xmyIjPtT07X zgAl`z(7e=&g4E=a)D(DBBJ0)wCF9IIh2oN;%;XY<#Ny=CJaAm(7p0^YX`)3ChUrB` z3Mq*ti3&NXdFdq?AQywU4E~Vhmz$cKUsS1(nWq4YVMug=Vnt8EwIZ_wl;#=2i@*$M zYAMNAP%Y+SD2GHk)B)uvN-(2ap(GzE)qqnPIL)AGhr}Q_`C-xF3{I9Q3aa3sDoRbx zFG>Mts$vCDx>qe$NGdIXxH1KjFj9+bRa5Xd6k;DF60ul_&54i@!bn#{>PAj!m_dTb zqRBb=#ie1FkPA#`8Jn4xl3D>O zJavmxOBBF7)G{`tC^Iz$6iJCG;M7x+uaKw!E}|96GfOfQoZaI6T-_Z*Ji}ZSic?Dz z71DAN(~E6EDZMzq2vnNqSSgg`7APbZ6(v?GxurRC(8r(#cC@R9*!BO+6RqYGQ2 zf@M=^oI%0_tQr#dV4s6)w6y%vycC6`N`;ivocNTy;`ofj;tUNciO}2~^CWS_~4f0Brxul^7AX{`CYNO9=M^iYq^4zp!ZR~Z0b)HYdonmD=7GxUqST!HL{RYqavNCE zN}(hpRiU^bH5pX%r9eVDJ|(kAAtkdYHMs;-1ZN}`Dl33Pqsmst8p6 zDu55GSIEpOE=f&H(IeiTg3Oe7P)V*p3&V?(Gg5O?X=pw;+~W)Ki%MwXesK0kOH58x zNK8p7N-Zu{(11jBd}e_rO>8VG&5KVgR>(}DqUkA#sk!-i3i$<~#7RwaL6;>^-&jZ; z5}#a>Lw&PLk_+M?F-U#0A&sy2;?$DT%oG|Jk0=^Sa^ee8i*k#p7?%a0mV0r2ayF=9 zgp~N1d6^}di8+~7sVR`waYm{_UTQh4L`%-kD@n}ED^@5iC`m0Yu?3p|YJ-CFB&;k{ zP|c*Es0zuh2n_rR&t%SfGG%f~CPjFue zQjI8-XQbwVYW-}5%wmO<{PH}A6Z7*J^3&27IDMh*vXYVxN2wxk0KdVnx~Kmb7?T7(^7;i7FwLD;1?VWH7g*o zC^0WTGesdcKP6Q`1Jd#=$j`|s1;w#KK~ZWMsK=O}2Ms|^=lr~qqWl~MXo3c1mds*M z-?CUi17rqBT|UT{P>&ZR7A5B7q~<7q!W7h~1eXz@4m>E@V8H{EEG$heO-%v!CP3;k z^U|U53rSqyZX~GdUYuG2s+wUz3i1VtC7F52MX9-|c_mQoL5X=O`MH@@sS1!@OEIXu zmY7#uo>~MAh(J)3E99n@WaNV!SIou02@wJ%GKJ!j#G(=_1&y545{2y4qP)}`h2)I< z{Nhwitm?o?5=nb%9>`3PheMnLps7irBm>mxgIW&~2L(bU%!@Fu!9u7sr9dI6G%XF( z7Rszjh5E}S6*LF|iYndwyqrpfs{FjvVk-p(jXY4I2etSZbU;HT&`hVA3Co^b44gTM zNvWVjSE;`h)#R}z_IXMbRsS5Bsl9E}R3~Gf}f_fK4 z`S~TF2um$0RscD~ii?3WvjFNhh=f97ad9cA%z|%Ko)a{5kY5Ul$sAA!LgXO1wk#3k0UW}`Tnzbn;4o##&x0DmpplmfYEBlVf(lkm z22OC%3YCNfIamzldX3UzP*_0ykXfL~zzHufpbkt)Ez3+!g_K(0R%T+30<`ohC{TdP zqLpA!UnJ+}<|cv@2PnZo1xt%zsZyh$C?8xD=a=WD7HKkYf};c+1(|u^990ReBcM)! zvSjQ6cpuGRN6x|Izrg=rM3QLfV$8#h`JHErBCyU7 zxFUmgkicU{d7yq0WV8#~mw}F5St+RMX%#c1gKJNC-45!>B!V&`q*o5@VnIyBuQatR zF$dj7X!{gYwV`T;TAq}cqEJ|xm;>q`6@elfRIJB?D+60-b4!l_K1^1cSDcv+>4TM| zrl%G`hg3nPL!uJWYk*kF05aP%0M^Q9a08DKgSzGEsU->s2~a5li9%366J!>s)23RC zuper6W&r~%DaL0OfQ?6SdQN6uHmDws&(0}LPeo|YgpM_WGCQm@m<(>ofU6s*-MNVs z@g?x)F~ZM@;Bpy@J~R!vnRz7YN7Vr7q*y^CP@yD0Um+(yFCEjHC{1F7FF~P=5*($) zsquNmNWqAq0#rkTngDnVfs78vC#N9PgeMk(2IQ@vBggT{@OC-8XAW&Bf)g#Mb(Eh} zoDUwD1`P-&mlhSJ=9T1Bf?L{o`3k9NX{pI2dR*AN3fd7JUyxaV6rC8}fON$W4#ZG^ z=%64J5tF9NQj3!Ei!)0qp*aE5f#8xW9@2I|n2q58P~2t~l$juuMCO+&Bq!#9t4z?S zmjYZ#ep(u&X;}ceDGwBSpkaN`^a^CS%rl_O4Av^vV}K|s2aRFm=N7c!oM1l(Zo8 zSfGNk0HHt+GjSHdn!fQxsmVy?KwfG(X(bh!29O(2N*a)SQBiSxL4FP*X<$_a>9?RN zM0f|}*Gg~|5)W>6gFC6PxjH=tc)^3N&Q>)OJpWLfUkYmXfZCJA3O0sD1}2C+25Z13 z7At_7CfL$8hOyA3M_dJnq&qbaRwOcD^&D6pG7kivh9u5{(v*Uv(zN2ts#GLX5rSxe zh31^x#7gjpb5g28aW1G^R-{mp0c-Oi1-Tvrq_QQdIf7p)f#wLRW@vMSsGw!UOJvi6Vkv&q)w7^F)j`GvTtTydOVsw&}er)DC|=eK%I^F%mRd)ic^#G z^HLNNi_%L$9avDi2;%JcXTyVe#OB0I~lJZM3 z6cQnQKk!IqBDh@Dh4>q(u!i)ap`KL*4JUv#g2EH3SfRAIG%+WqQXw}n8$2-JrJ26-x7RK%>H$C7@BkUBqipi+G4uF3B5asW-ykF4jN}H zDn@JHBlQk5^Ar+c?$Tq(1r0gorGtEpQdVk!8n&rLsd>q%3OT7|sX3aU;xo4brjw}V zBbEXf$po0yMrdVpaX#FAaOW8pp_b#wYKW=h@ac!S!vvI%km?F#JMhWD?Z?^r#itu) zyD1*q@yQXk9iMKP?Phpv$0tYFc6_>FwwvRz9iJRw+wtl4g(ZE+j0m{QfYzf5!2xc_ zk@Z1J2xJp_k1&_|c`~jNr2bYOZy-<(n zF<_L6;7TMuF()&zI2EahfW;ZG;XhP&z}Ec0WK&WLN-`8QR8useQI(pa$KaU<8GixC zK6qR-MIo^SG-H*jpjv9hrCN-n1kA-^e_krMtOd<8;0l98NW?%R6BJ{h)s{%!0w)G& z{{vQ+L6ktcg9;`2>8T|dsYQ?hj(ji^tU!^WG!GPt>3QG@Q`j0mbNi2DAtUG;E$!1YYa_UPA^`3mVQx zs7y*t&&&f2s(~sk&~z}gSpZs6nv)Nnh5#Fa(2)x2fEO!(hGU_t)1X?w27?9 z>4L-}u)iV8s!Bltqgt%T6_Asf2wI+(pI4ljl3JutlnQFIfkL??BePf`6|@=_?AJW- zN>)&61T8uRO{%9B73*;^lp+>=rsjdy$APjf7U9gig3=P`Xe?xb6KEzO2ULtfmgFLh zoPj%7pcx%dQxmkP4K&yUTHXY5n3g_th@T-ZHJzcT2-?GDfRqwYqcigqRErffpuPo7 zr4}R0Kr8^Q+Xby&Vt@~+F+j#X8NkDG3@PwAL2z$|0WyvkpPa$~@7Xb+i~};D4w*AR zhqM`>W7!}F$3yZcs22j}LdSW)?2?ik(1<)JCgKq`APq_}z($xE5Yq|_u*FdfpwT+W zU>%qV9k^ovx1OP6s!$Fnj^k7FQjode5h4cEF)jwsC~9Icm;!keIz9kiD+LX9@K^@} zVmS;r{YH@O6L8?M(UW#shnl30IFyw(&?xts!6vJwm z;?lIV%nCgQ*!&QLp9(4hQx(c}QgkwOa&?OJz$>sp>(C*+b_PT@nE{;G!EsfVn8T1+ zzyO{LW!xYhurWl%W-SskkgTD+T>1Fqa)9aY$ZH6_(z zCFt}AcK(z{N z%K*p%NU%cNc%WnhTDzH_2CMs#rrkh`4q!SxeSBQq9eotR!N-sSnQmoBNiAbY$xO^i zfv(kOfKI5UFn~q{7(lx`7}64Ric=ZV3RCi+g-jZ3<}(FUC?uAoGJuvKfYT2oH82!s zg7zgql!G!Ds3Qx`T%{=m3?)USsi15HkuFUsh%ZSlU;r27=o=?MDN}VHa#38*B(@mrG_IWG)e! zI3P|3wW32^0zkE18Q8G}pbmCoN_=urYGO$$bi)i-24OTfYDs5I1tpgFXnQgHi9j|)0u1{zBOEoOqm3aScFNT4_k zRT`XlAdAkh=>{#P23KKF+lv*73lhsAljaH~`6Y=tpbbu?;58heiJg?9{DOki6i_Bt z%>+#Yg4=C`6UX8poq;&0d)o;0i=;%ker{A z3boA*v~j=+w1~quF()m*2(;!1sxB`-uPU`DUm*%)FUVz})_oC3uLi`WkTqMN_1cLF zdHH#|sksFul?srBVxZ9`P!C9vivfqS(vpn)BG5!Jw0)0TWlBmW$SBZC4amZFD~05Y z)Z}bX@Pj%T{sAEh1;weQDfzlVL3&&aju;w1b6TJl1b8V+Vv0gaDJXmuN{UMJAgdY_ zvFd^^8v-SL@ETLq6a^I9is7z?D=E!`u3kz_Q2;Log6%I-O~F(Q3N~;cgH?h97u+Ys zREe+_8eUcq)4?eVv{uhn0hB^P8$lFY{B2cB!2w?k3OI0E2einnFxeK=Vpc8H)KMrb zL1KfJ^FdQKB(~tb$;$_&CWtY5T(CVikm?>(U_;Uhs9Bm?4C;L7F{Gq|k_coT!V09h zs1g>?kmWgrpq2w@Fu5=}C$SjR!9dm$p9qgVD^O-ai+WU}f`XtuNIfnTdr33I#WBQD zAuTgC2Q+<;(=DKe1IWjqhyZo4L8B}AB?{m~uZYL`D2UOZp%+k>s#u{kFFP;4JP(v* zz)_zBYOQLh8kkgoG8s<$gAmRn#~GP%*?X_HI=}-3SkSM!25;J`rP0>u%Ko}a!w{>jc+j*sQC%o z_zD`WWB|#76C-Hc0-9-ypw*NftR91mGJrPXfzm@sz5+-WvZ=6?4QYvjQgSY+=Ti*s z)>tWk7Uh68ucW4cdk6}t6(y;8DaD{!cu;>BviB1?ACjay7gBFR(=v)yr_2&v*!V4I z9WP{G0lb*TrUkha2lw~DTQ{+(%uh=LH8a6`*bsif;~GS`zycyCu?Vy>5WKoTwG=X@ zjLj7cpuVg{H|(C7k7f~*9M zd8120w&;S!ql!SIODPPhrFw9bnWqbC+3O`16(s60s8;ByR_HNg7MMW=5FImUn+dv{ zFdCYEVi~j;6dWBH6hfRC6oOnB6oMQX6dYX`6r5ccJpCA)gBhGX7(9X*JR{tK7#u-_ zucN1r69b5J1dRYd(jTO10<~!pOCY0Mu%<4kQT~I)R)8D#CD2{f44_s5JYhik zVhTm6X`ofa;9fFlPkwO;XoYNQojMQehEWR5O`G@*hG8^N-`4R`oM}6QlOg~ zVeSJt0@OzbwT}`(O<2&7Jb1Y}#0?;MXblaiQb8-(84L{>42>8JjTsD07z|Cpix)t3 z6}0)m5De~7Li0AL*`!(wPSXmG(B`j#Mo}`T*{aEf)B(;<0##a|iWro0L8U)vi~%}P z3mSjR%u@gr>L5`_2Oz#EwKzYgOhXe(YXPUqkoS?$F@J14lVcP%n)3X2-%Pcn(qQ_ z*9XT6XvYG06Ne5oA%I%`KACyhp#BG_AVrDLQ zUI#QU!jP1zfa=_g#4=FX0p75XpNH5K#{lYoBjpEBxmTW%nVbR2HARrlH~8QWP_YV{ zKT!qkZUarC=qMB=W`Yh20Es3hDu5EH4tU{ZVnIP_ViDLe&^ZW5mlU-5x->}_GKBy> zy8?^lnV?uI2KD)ISqpIAR8RrymybBe1~r->jYnw7 z1jzjXt6P%c*)?46u!18Tkdpki#}WhlnI+fDYgR)uvEqf#&~mLG$ZKK>;fK zFh)B;=jIfGoB*okKtT#FKR^i;RKdV1Fi`%2lmyVyGBXd-908v$1KlFe#ZUk~8Ub3{ zSt;bEf_ERm22wzCV~}zc8j?ED(gm`b3v_yqLUMiqXuJo_puBvrwcwD4)!3P!6$wSz zsVNGe$OkRUf$p1uBwg^fOi;qb*4GCmo6>^#ykd~2u?7jG76y%%f|S6kAjn`g#1kpd zMFd!DB8Wn8aDbhj0&PsBQW7fg;WpF~fzYsM9dubBxO9h}v4JR2LG2Jd1>eNVq*QoQ6t-OgRFy+24shPAv{wLEj-Zqd z8oNT8eA45BEk49;3#8}+mxbT~RM;pXs2l}lL-1gVo&uyQLQ+*g0LGea>azqZrV1K?)pj ze1pm*Q2CHr1TJ-5GK)d$aX`TaiEwbQ97zFaY!$MK64b_q7y%h^0r&DCVPdE0-1v3O3+9Tv^$Gv?|{^x z4dj7FuOLF8!Wa_2ppjx+R>LzCL=#9UXz&)I5jw~TvIgQ5Xn&^|bemx{GEz*O#vqi&1>9{2he6#B9i)Z!!4x24w0MSWahQv=0t0EF z7+M%85{NzGV;5mf4$_z{C|*GarNLT!;LUyD)-yP1LZSzpp24ju)QSNzW`xwZLst&j zCJM?J$O#voGZbKFtYv~1uYo$F#R`de3XY(H3DQ?60=Ebuor%OeP-X^qRWtLzYltCL zk^-ns0+n;1J_cy7D(MzFI+AZE!4iuLe8L>aQSgQzsQUsg^B_JeDaip%MS*sygGx)t z;ulcAA5x}*);++NvLHHK&@e3mx1%8kE5U;nk5RB@G3an5M2n@MD8DQ-1;Z53!4)8v z!-j=HEeV3YKrWTwEW>uS`6+qKubc5E?8zUB9uVZf*M@l$w82NAeuluAcz#G zEP;jxGuwZ7MLS8Z4{niR#^T16~h-UEgKWJp85_GZ`v<86oTw#`h z8iBA;21ranZA6rXkSR<(F7yd|#AH1eLt1iPNe*P_3Oc9@ZKsxiMoY3&p_AUAx(4j{ zJl&+6eDJ&&B&mbPXY=!5Jx}O37U(=KP#uCiSey)11>O(@UGTsK9u6 z83S4wg6xG9Q8-Kl*$f?KS1kn%Ac8kH7Zl|uB_`!mDkK*rg3nQe&SfVmB$cM?DWEU* zgyl-GG;}Bj>aY>FtdTxbYUTO(s=?8-r z{4~891qRh(@D6HFy0lVYNCq9_n4FlyP!8GS&H(D;GQbbq109N2T%*9CtDp*^Ad3w_ z%aB3sVo*;UdiHG+s6SYm2RbnheCPzIHvl^4FE9#v{I)Sl)Z}~r`h5+0%|$f1|sZsK^)YokeUNNO$R*9 z0-if@1uu*Mk14?9K*Qz;EesG9&{_wq5EKB=HH!=}ss_gSXk)z@sW}Cp<+;fWMHzX? z1(j9`#YM?*D-{_s^HNLV^Gfo;=gKhH>wzJIs)11jg94b$OwKI;Z>Y3_7Bm`~&;bT; zU?NMWmVj!d`~uJjG^EtURFaocl8<779-*Oa(5gujECZ3KCl!O-p_dF=`UYMW4Qe~X zV;8cB4B{fBHC4J947$3Y@L|vmW6;%wHoh5jlNoe%ARe#_xhxbUJ z<#;h{H!~N5t_g##8BB++nH6;B8sSp}xEOR35!rbw3I@7c44kh(tvk?6 z9Q&nggh8mI(FSP>)!rVC(! z3W^d)RfT4oZUHpR^tc#w%V5euoBC2R(=$sD12d3V(=CH42Q3ZHPF2t?GXnKsbaP=k zOY?O=B_L=^aVffHP_?R{n~P>bZfQQqh|+w9l6;1eQijqJRI_vxN=iY6XDNnb@S3Na z4hkdC;f3IkgDgnb)di;lT~NV8d`zMx0W8N-K%*5p#R~NxWPb{@9cKk!nF3iC3d-Yf zDNw!vuL%bw&(gf&)DpDmJv|21OoVFCJP&BuJ(6nuvcw|&oXjMB$SHODkbUTgtO1$h z0xc2%9r6V0)8*tSfaWbTlQTi5pCqL!fL7I(f|iUY7DK8LSX3a+Q7_E{t1-+|U?fTtADeTbnFwCe)2w;naP z^ckRoOvxFb(_c&S!JCy4r@(^UsGpKrrk_`ulY=iHz~Kz)gTYlJI~;oOJ0$hP&#OR- zC9o_?Y(jF0YB6Y*7Ch0Z$Hic%XQXGDs9TU&#NYzTRtnHzt76DXP|(4?u!%}gaSCcv zL6)P&XQx(TS!V2*lY>~13Yt#^?Nk8GKNlCLmF9rQB8xNAL5D#idnhT5WI8KA@jkwL1gLO^?Pz^gdZ5mj8eaDo9fIY5Vi<|$;R#g`UnKsNXwyoj!-I37G90$Tc41Xb+F;1|r`>%!pc z#sFSv#t`i9$l&Y5;Ooxd8^quj>f^%@?j6h!5E8`T0a|Rv0A6y&;2XgZ5)r}>6u=*zh;z7ECJ>40+T_YI+A{g8Q{DT=B9UUDReEgjm{31da zTs>VF{5*X@+~A-v2FDOzM+We+E(Y%i24~lx5C%sx1{ctE`Grv5f=|76Z;C}(I%2ui$= z!So_f=?0rqf>im?oB~Q|TnvsSC7|vlsBHr|B|I5)`XQtQfeiOR_OwFS@M!>LRnXL* zTP9?w0j4BmC=7I>etv!l%z9`e1$3AJ1HOY=!5cF`C($WDdjjx{BaoG6U~UPr8)0LK zsl}i{c}(Y^Re_-K1CSz6c*H{r77f^)2e2&=pb!C%4uKSc+V4o28Kwr=3E<8?NG+xsf@b?PK!qi^Yyv3-Z8rifOMtAc25;6Xg6w$%tz>~s z<)E4bx{n03wF7y95O_}+EGkfxgHi%^bHI8)TO527!BdeS|A8k;6u=|MrJ#E+%JUdN zPK<}1N(su*;5s2sy#!W{LbYL7ioWO+qOMpk8M?kI9yG=j4{Bh6M*Q-03qS{!few^~ z?#E4pBwkP>F(nmxG^#>o2?K1c2*f;4#e=jwC=q&w3@G%V4n(eBKnH?CIx3)G;bL$G z9iI+L44whRCoV{z6uQBySVL0}v_}v$J_|AkTDEF{hGA`0i#5SH6g19oT

    #ezC;ohV;~uBJcrYC>=e>Fr|iDe6VYXn+r-O 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 X1%zG$)ek;13nBz2VfKPK3=9kad`FXz literal 0 HcmV?d00001 diff --git a/app/src/main/jniLibs/x86_64/libsystem.so b/app/src/main/jniLibs/x86_64/libsystem.so new file mode 100755 index 0000000000000000000000000000000000000000..f43858511e5dce08d6e76ce21cb39506a8c32eff GIT binary patch literal 10408 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�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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-anydpi-v24/ic_vpn.xml b/app/src/main/res/drawable-anydpi-v24/ic_vpn.xml new file mode 100644 index 0000000..913f164 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v24/ic_vpn.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_vpn.png b/app/src/main/res/drawable-hdpi/ic_vpn.png new file mode 100644 index 0000000000000000000000000000000000000000..c7fb217f5af3cf91ff283c039ccbd875aff4fa01 GIT binary patch literal 662 zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?#d^9phE&{oJHy&T)={Lb zcG1D-uFd=bF5Iq*BQi9@1%o3p^jVH3_#{tOOJZ@nbfbl%sdYi`g3d)r3pWaLdxrG1 z^0Ra|OkkN3!SSg26Ti$ugERN$-rOCoYx6<0{M#Jk|8wuoEG}ETXx@?)qi7;GBj9@xn_QvM#Vhbw*8C__24{N$sg0;|%2bI$SPrPCL+A zuub19#;o_a*5k7YXS*jvT;Sttow;_y4eEg$3m!SUzNz`=yhw>Hx+zb+S(9!!F@|(!z67R$C9)^TLeNL zZ@RRs>C{{y7Tx@bvlmnqyq)xz-=oay!^xn4nFsT97X{zx{1R~Rx5>q|mo<5gbzGmQ z*I*dZximMxF+}&fopzDf?25L~%=;{g|8!4ZefBKo%pCVA*Br}Fm+&+m5-8r2wA1Pi z3+s%}YnIJ7+`3bscu#VjubOw2+qv0%zSBYruRf7TsS8zWs+;hcx8hx3h=$|tC(6H9 zh6p>#M?5m;vlEVEN!&B#XN~b)R+|?LM>1>dc5y^8v%Y${`p-na*=sFNGwn9LZmlEt zp)%`J(-#SjS2pu+a%eGzi`1;Vrv1l{dB2W}Z*X)``rA0Z4RO;Rh+NUUZtQ6PBRlf9 zSc>b1s#SiQT*Ot3;yy-h6i_aYh}yBOQBG=~WwZ0_V);kPP7Qnq=Pd8{{hs=t@nlw- Vu`TD6#|#V%44$rjF6*2UngD*XDQf@# literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_vpn.png b/app/src/main/res/drawable-mdpi/ic_vpn.png new file mode 100644 index 0000000000000000000000000000000000000000..1528a1a143ef62270144df84b803fa1609e1723b GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoyFFbTLn>}1CrD^L`2YXE zjl&r??ZfH}AI%i(SdM5ukx*bh66WxR;fPd5QJKS?<|Fqxk2D>T(Be6g>rnIEUd@6x z^f1ejxehkG9gi1QXbZRt7}*GQ-2Z=^cb(%^%SRLb^B!^9!K44zUt&)CD&8ZD-Q;FJ zRN$@e+^}Fu3U>ojjKd$1{x+`+3F(EF9U0u8ICew{clY-e{P@AF5x+p&uH*OzbB8nS z%I91fA{co;FVs$Ac_i=fhpEGVAuG2Bv-(oYK#8mef(l{|o0bT5zkX6M!%%kBan9K) z_934%rsy|36E4}n)Gb-Out;o2%|}TommZghBHr%02$@rlHYIR}G)hjAI5FvUi%tXE zNBw|}P5N?43uj$l)Vcmh(!t=2c?Qj^GMl@$Kiu3P`NU|&Aq|y}E;faXr2v zaSD6+K6CgZyNdCqkh(%$+wn#93z#O|P?2WZ%EEH3QKz*l{%~@IKaclg8W z9>X$W_Rkp$1^0`dEeMFLXNW2*4BUK=HPvmRaTIBD zPfs#o4}ToEBK<{Dt4>(_E^QK@^x*C#Q>`XDCoM0hYX-BgTv(`_V8?0a zvF ztIgD=`^`Phf{82_{$-eUi-V>whoT&G(IUhkM=Y!i$qd8m(0f?N!%(`IygW zt}A9)d30UD)rgPHSL=)pFhs};Ox~*nm5PZB-bZw1S8XY{W+d#>t_@ST0l zrxP^)Z%b8dcz~Q^Yi80xt7mgeBAMDR`$A` z&t_d!J6)u6g8LSm7F+7h5VwO%8vGjUPX3Qp&nQ$mJd;(5qtS@x*U}$y`|rti7CdsS zWjVxuAu)jMXQa#u55|0!EoPS(WR<%A^G2;>+&H_Znyafz#o_$PL;kONX1gmaT3dFB zDanC#K~@066*jJ>DhKB$*;gzBWLJooG+!)Vmee14%;2A)WOTaul#mN?0oE_NbI#3c zJXQKEa)HGB0|M7sjkcZCGx)hc>_Yj>{-34Kq8CZb=AE^6v6#`fHCGdg9Ji(LkqZ{*=w#PC{l^%pN?K}CX zm+gi2am%s~ZI7A%PF=h40~5=+SE<==Gj1?2xtnhKx=me&p>gsg!+k+#CH6@!x0DmG zn88(*eK`KSX73Cc))lAOEY>c_IL_SEuC3GZiR%jEtwRFS|28~0m?C77Z+$4yzl23$ z{(}8eKCrtO6&&Vc`!X}x=Jr{Z4Z#oM4l@VvAFmLb#kt_{!-g)4+B37x`pi@P5l}H9 zd1bo&&VLf3VRl`|i-N^7?-i^UGT~-2cG_LjCcVJ%)ZUJ(29f)XM}5x4U(Pd@f5YlW^m z%k^y!60?m@U6Yb9*=$vh;X3EFZr9H<2S}z~+_H|-^TNaj{c*-k=d9kX`0?f`=k_WS%hV%4<)o+CpmXK4H|DE0n=}c(aniPv13) z(~73^e5hjH6I^)j+>bR+7#z0vJBsQ%IEp^Mac)uuvx;#;ja!^a8pG865StI6C+sqFnh62#jytd zd%j^6`*!l3;+&~Id(x5P@kRSI9OAqtSiDSglmE^#cSE7QjzY1|f*9Qkw_KLm5ACu^Z5C00s4je6Tdta=U|Ul zm|FIxUB*_X4Ar2 zl5gHxU3lbP0l+XkKOKCo& literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..e3721a6 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/layout/action_switch.xml b/app/src/main/res/layout/action_switch.xml new file mode 100644 index 0000000..8ccb937 --- /dev/null +++ b/app/src/main/res/layout/action_switch.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml new file mode 100644 index 0000000..d558b7b --- /dev/null +++ b/app/src/main/res/menu/main.xml @@ -0,0 +1,9 @@ +

    + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5e8ab69f486ac4b8c29dc4160ffbf76bf92a1c GIT binary patch literal 1085 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5ISUg=ELn>~)os;Vk94K*o zx#SnFx(PbMLW}l^C~`E(t8@y9ELC~_dfC#QmsYz8s;a4ZBu1Qm;jEb{DYCFF>Zj1m z@-T0mk4u|WxIEYTR0V!c&6~WuI4^2;^p$meW#{Ly|GTsCZgtuJ{kG>Vcc)v2s~tXY z;J|?iRSn(&a_^5#TCtqfaq@Kbb!+P0eEybpt6@?$XYccQL^JoanMq8)mXqLl zCD_O7uyfMI(sj@GA3N{oqtRX z!UG$gcy2xVhFNR3mQ+AWkT2)!2B*c|rv(L=oE$hf7!?&*S{ejEoJnj;9?WulsbD;r zkM%{uIbn^xAGu~lu`O8pSU6*TUCXOqaTcZfgoTVv&v9#f-XZob`&VL(6TjPI6CeNC z-s{bs^6zuZ(XP`=nI6n&6?nt%On(%sakxlAZnC~g%7=4|FEZ++_ee|p)s_hM^_5)k z=v(wR#*21OHD9LZG#aly>B;;=#3orf;9#rY3-iCt-EfWJ}eSBX1=UBP5R^Za+ ztrMnwy^*$5uKLUQ+MD&vF4n2p$M*BRKH+gqImP2(t6s)?IiZF~?X7k}^WOi;4L?6S zf5ZEN?qudy_5bf4+Txck;HGu?Z-~3xq1D&pU8g>p|D`s^E8}`1qiWhGC(g6B9}g}p z^zhg&vv6JLJ~syr4(~ILR+*pf+}LsN)rlX81wK}Dx9n~Ec}7OJWbdDxjsHJ%@cD_A zdZf?TeqH>i?UK9cvCd-0Ym{fsEZxMbZ#si1rN2A$8H4VZ686arVnv5^CZ>d!I_vLw z@iR&7*1d&y!sZms{rh>T(cK`6%>MmZ3h-EfMadkF2{tQlabC-cnRh&@_eoTlaYmYD zNJh@gA3V4AZr|1Mm9obZ^gI=4t1iKjYSmi>o_* zqRjLn+ah0=*{bV&jpJ-=ZMW+zmlJ=WCtlZmx3=bPcvbF~h=)7trcQCS4bZmdKI;Vst078rS+5i9m literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..795f5cc2223fb69ddc93fea562009a70f0122687 GIT binary patch literal 2900 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 Hl+XkKs-c7+ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9b1b6ebcce846cd5ee3b871ab36e40317e6afce4 GIT binary patch literal 1807 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F}vjp8m4c9mFm zqe&#BK&Fy|FCbunk#j2B)K3~g8Olprmu1fK4$|0bC~* zi^+OL>$IAAu5`M{)nO2z^_baRQ(T1Y**mDW~X)cV`EnEPXhsq z_Uw1Jn)oh=20U1Ktm}D*TJ)?_8F!Z%O}!n$=H07xIjsBt^@~i;3wYmM%+1NX;9jDX zQ1wn{b+6ghO9|S6Qfql7B!Z-L4zYf3+_%8_Z<=R*dGe2s34i9yS?wFP_f*E}XU*HT zJ&Y{l5X?BO%Dl@#y_56Up}H>qyo6P~)217|@M4lV5WwcG&3x+7RMtCRLnU>m8no@+ zSj*P$`>*X(!q&+Lr_M|*%XhpVzCq8aMUJ`usWE4syNz-5$u)=9Y%OQ}-?6p#;@N-K zr{`5?2;Iq9U=sC>sbc#7U(yxs*OeY?d~yGt$l(|_serZrQoy&BGalTX=y6ixoSF&? ztJ%qqH^jwRnQZ!7F0yTGd-Aefym({fna@-9w(>;c-i|{P2Bg!Up~jR z?utvQoc2qtef9ezJT5(%J}F|s#>uQsR{IN7%I32zoXIX8pYgEk{e^F~;(?hzLrXrj zXTO;7R_oTJV!4;o!yIBSOJ<5LebFVIv}E?-x!uy9-kP_Rc!TvS`0iA$zkQ+a@AQDT zy}M65EGU_9|NoxmP22wW?9toUIz{?MuaL-Yo^w;YbQG?O=8GHsHu%h{x_@DHuWsH! z;g$O@w-goW|Gdlf!CL+6>A4%Micfbdw7$z|6=2FXpX281yOy8jz0$=VvGrCj%b7eg z+5HpFtP7D+y?v?u()248N)z@yv9PLr=f=L^dj1_}@m2pFOTYTczMFesd!K5tz~Q8Rnt!l|4JUJMSZ)cIi->L z``!LJm;L_&&+oo}DaoDpzZv(m@0-t6s7zR69V0EeyJp4Y2A6rVZPIr>xiB!?dizK6 zU&U?9xY>99#Ix+Y{+DIV)6RyN@4m5Daz1OQH%eYwGx5JU(?rA5hvu>@N?%_srk}Jm zX=gz4ZWV^fMc?zf3&byp#m@in>X3nYZYSW`9);=jazlHeyi1MVImwPu2fBb>Fjl*ytb`dq#D+_`OQQpx;JsZ3CYrqEa@rez%eV%sWFM$)s_dWoLGnTcPn>Hs+UJTV6jnvsgDn&fHO%Ip=ZdO+}||V!!=n z@6FSF5@R60#NZ?6h0M*ms*gElX+`dpGpM{1RkBn~az;i#Ps!`JF2#oe(^+`$F$zn} z-JmGG!C(fsAH;?tTl-CFtj<@IwCUbF1>Ejgra@L8FGdH18L`VF#;M*=<^PRUIa-tB$unYohQ zgDvY)d>t=8xe+RVV1`}e^WDJ}{2vZ~;Fzj3?^fv1h{LaxoQ}7fXSYrD<6*IQ!8Et9 zYi?-ru|Th5&ktAs_%o?r`o=>e;|UFO8!k>(eZOYj>{->4K4+s^I-Xx&bD=Hw{GLY3 zCWq@wZcNSI@XpYH%{WZGrtQG9go2ffiVJI%m6q$r8hZb$-04&O!t9K~=BDV5(p#NV zSv2?jKD2Z12eU7%vJ1FNxK4fkw}H*tLtp3bMjQK@A6p_P8c$o_<9TO_^!huoGcEMj zt7SU=yKm%s&{x1>`Z?1QtWnY_A5bk&(4TvG{##+CU&rO%=`hV?VvpfB^Xn*m@VotZ#LKgB+>K@*RF0;I d-~ao^{tpA=Gm9Ff9tH*m22WQ%mvv4FO#on0SGxcJ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..30bdfb1cf39f54e889e40bd61153536b5db5264d GIT binary patch literal 1455 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^SB%RF5iLn>~)om=lSrBvp) zqk%-hW>yLNMNZ1>5mGCr9y#!IhRcl!dF|zYf_Q8M7Hx~*D_s$-v~63#N%==Y=A{C9 zoJ9eJHCJOBOX zz0Wp}vlAPg76xdDaJ4$w+D)B$^qJ>U*D#NXq8n$Je)#2fk=6Hrs)cv%+WNUODiZf8 zh5qe{iQKG_7Cm*jW!ty85(3>HB|7tFZ2S1~m3nfJ$?nC$CZ#spE2}DYwXU^v-GBbb zg1#97vp24BdH=KPN|yY+2xb0mzd&QxO&Zqu`AHqu> z1cx|(O1a9BAJM&Hi)S+wrEw)WPBTm`1tyoE=n*VHcMOO}+n`ulKl zU*#wLcNcm~Pp%3)U{)aaASYu=MfvCJa~l`luI;hoYT=J=b9tisl=n_+!%EHxd3rCS zWxss%N>XoYsLa0q^NlLUjJAfBNxQX~>?2&?91%#^`t0qSQ~`sz$`4BB$2+{rKElVO zz(53Qkjo0%s4E|~@l5U3HzzvX!h-6Wr`DEF7M-{67@yqA^q1E4Nwe==cR1j~5s-G} zg~Rr3-;LImP3i6XXYcGB_3gL3td9Bqc}JILDmbjM|MB_o^c(lfe;seNi}MiZJaMC# zkEuttsO$8sn-3nGnDVgS*ZsQfHvR8PEH+tv^RpfuKDGGZ_2sLp8{XcE=VrWo_MQLh z8wVN+KYY#1e0_E99>s*Nn^V>$PG4Wv$rL|-QU1EuZ6+KuoQn4Sxcb27d1K`H4;-&w z%RhU6)$i9m>$%?}qMk+8sf2V-o00o!r@De!=-E|u|B9Vvn3(*ncqe9k!1ZE4>5J2B z*KT~_mzJCv;Zp3~|E@MkHTk@LvA@G5_p59EhTU)3`)~E7*H(f2b?e$d@lACpgwr4*A-jWZjQPmQ+_lTS^s9FxzV2(Pqnm0_p$ug{q?CG1X$ z^``8P31V_*nix15h(Hx{{#9;Y!PIpBR3sncWoxFewSM)nCX9^>BkhV(Q`K2~d>vBm ztkl~dzWs-^u0urK;ZsMSiazi46#W`;DCLu?PshcopBuIN`t)~6>o%OaZ$D8?GkQZ@ zTtSDqgSRQK_7p9hN1p?Ja4d{I;y$gSZLj3M{~CtEHzGR}4;)Mm{Lt00<51YnPwaD9 zD!a0t?0>l8(i&%;3`uQS;d5#}WjoIPt+cmd^scF0>L1Y&FzwvFgIkm)?A{(fMJFck zf?D#-h=5b7Z z8sFWa`{tfN{2h78_&e^=#a(stf3CRTtkdMQ$!}Mc4!5EkAD`Zq3A0yZdrk6`wpQmi z&uVy{+L@Agpg1mDNxZt`+GGfVnbS9`JN#I<{M4=-=q z#%=R6TVF0;PTrW`@Pm@tkv0j=HEByuXw9ASR#m9ny}CMG+}c__T+ZCMSKClQ?1)Q3 za;71-Yi|VS<2yCR{aX(tg&kN7De=zs{t~y{t!EP0dnt;6fq}u()z4*}Q$iB}uzHbz literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..5a0718b9f0d9b6a44bb17c287d0486ca2273eca3 GIT binary patch literal 4043 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^U1CwaO!hE&{o8_O9Hd|Zsp zVB4*0+tzQ>v^M5VPpSEhx>@^FNTo$h4U~T;VLG};Z|4a|X($o)Pi{38$dQf1s zztyR!?7tE`C$BbHe=20wRin_h1CEBuH4L_E-g8Rxz zHqnEhbp9@x5hkaiZSz4c?~5;&4fm#Hr%D>6R|)uv>h^0qQoF+Y>cmp7)uq>B9u&U+ zG)dd^bAfXHRYw1pi&P!1ujkPV6?DDRw99F3>DHrKRkkfvUxfQyj0zXmJ!JSVVIJKP z@ZVANBX`*yAwLeC4Hel7W@_!3en#W-VcSgM_AHLG5i%e8-Tb4fm|R1YGMKlt{$lI) zeX+yOd!5w;hexHiHe`9T$F*myW&FgQF?rp#D4F|@<-SO5zrJ1X`0PXO38$9^MmgS; zx#LpZ{fT>v(gp1$tGfC%eneio`MT6g?6{Q5sZCo017Cd;TgdV%M{(vvE$#1y53Z}a zotWk_`%9zHvT4&F@u@#ax{zgS|2wi*echGoE$>&aon6Gk9WBYNuVJlo?(Uf2I3zW#N3HpO=7!-qec z#5((S7#ch~Y?|9#Cbd9$YFpr;BYR?BXOyH|vuVt-SiRs+=&OR;YCq-_s)nZOeq+yY zIv2Jza&1FZ^~_B=an?x0&icQp@TlbFFH;VgvitM&6-Uag zOmAyWp8R(6vT0{itL*vrG5SRYE6>~|uX@^S59 zUI%t^S20g}_*0chdB^@QTX*$LXWy2|eON17(b9WT&6Wk8`x^RET{@@l@(dMK|9Rs0 zj}DHw%f;K~a>-76o%_K;@aeyvKen1Rckauuh8R>HJo3j@!!Kr1>H_@>Y59CNI@dgI zmzgzR_OOF&*)jQKpWCLhWP^o01=hdnsr)8w7w zYpXf`*8S8k`F)=K#lhyRkkHWPfB$w%-Q64Bns;YU|G7E4d&}Pao*LMeRJHW#r3*K{ zd>1_;X>BI<*COHl-YawV{{QNFfARaiSVw+?3tP8tWjlHHHva{^H5?nnvb)!Z5rn6{9)Fr z8#PzXoxAU<&wKHOy5}}CmiZ-00(Z(dzxmED?Dx8aIjdJ5SYGt-k*rDOwdY}m`8<&uHTI^=le1sY>wi1_ z+54XtrrccrYH9O}BP(C4vCluH_9j^8uKWQ3Q;SC*XKG(h?qThJ$~;HpreOA}D^s{M z{=IMyiH%J*V~yGKMSRxJ_0D4UR$S|E><>%oiItO{rXc^O)@kt_m0<7s2Ysm~?jn~> z*}m7EVD6i{r_D=SU*YU@@r&ov^Cd-=UAuC{=zsj?r&}Uf{_v=DFOv}Wn!Dv=i@U>$ z@5#-@C0~j?-e}idYT9JHEaHJ2DuWN;^@{(DU zwx7o7UJ{Ky*V^XKb!Byf#6@YxpwyqK!LAPP*V|mwXqfQ!Tzrkod(&-iEtA8z zvN>5Z+ExB-{Cev0#GQY?XdXx`$qJNT$ywaMd%G@4W6?sky22kqvrK&JndgVgJMI5l zcic>*p}6tG#PfVrIzo%MI()0Fd8*&o`z~ZCcw+r+UhbC01Dkn&39gy**gkGMCu>J> z`5j5Cq+jj= z-F5GY%}kX7&uQysAHL~!Y+2jlXjjHJdp;aK^JlL{*`L`P60W;GZ{hiQNz<;jvn6j{ zO17}k+Jy{KjHyp*+mzhT&*PD3@4DLeEA5C3d+!Xnd7%Micg2>gL)-B2tw!IxWqx9RK+-JXjJ^40w z|3#hPbL%FyMoR9qzPMq=b1uec8AeOfZ3d?;4J@|l`<*y9ySXi@IP=jX{yyXD*VO~^ z=5eb3)cmjReLeomdojMl7tZzG(-oG{VbcEKJ1zg-iMzs$;>s>dSEjvCU-}}rXQlkX zZ4Z@ALUrGNSHbMmX)%ZHe=SpIetmoMlDG@jCs+59KIzb(d!qqf2(rVWY*H)Z~I6X>mE{VFFtbK>^b{P(zKUHd(G)3vy_VkS0H25;`!o;o>W z_S3i4rCg0`?=n9Mny%9%-CwC~u;%;W1GhhHES|<##^U_A%vW*Wf!k-#u8*(!roQ~g zo&y_Bq{kfVnjh2hZ$d?-`PAR?+nXjlTyRibDLFnQ?b(0vkLG!=Cl^_L>^r3VIh`@> z-a%)Zo_BxeEVex(`uO?+mcox=ZCz8O?DQ`fzGjs*Q7e3?cc3dFZBp#!H-G;gR&0BF zXJhjE4w0YBjBm*%U=BbQXs(gt({$pg-^!HvfAlg zUzVNLD)G;!%|pM-s?27R5M1N4uX@pev-&5*m97MG=I>#hH}5<9)0C^9%S?2>wH|HY zJ$ORrw`X!OSHZT;#jT~fd)?K=bDL6{IeiOX-3@D(PFVAQ=8v7xjKX5l&%#1B+h!hS zES|$YGwQXxd19vKnQz8^J9RY~4;;@;dspLAwVEke@R*COj#*m$Avf!V1`|HA-?+qf ztlN8ER9mZUnb5GbLjEq>GF$8Qf}Kd zKCDTbQe5J7(VVIAbHag!gp2p)Tv_bRc}4U4flpU%Oi}%vd($xc`uBu${hl?#8SK+$ zet%$bSFHY!kjRS{uWmEXGhp#wDBYmFE!F8hdM%fCMv=$0`8xLlOm))4T4w#7Iq&?o zouLQoTl((Yo&Vy+6BQQKmNwpLPkT>C6teBO6t%YTTUAE0M@-GO_X?aUkqa-lWn^bx z^lSFou=%r)Rb^S*+=ag?Dp~tF{9=A&R((_N*vO_9zoBVo>bEqmV_BC)7L(Rn=S~wGAVj9 z=jvSMU)>#xYbRZ~dpAh)cz{r^v;41pywu?inV@A&36FeQXpykJ%w1s5y$weMZ5Ag*wNAB31?qJ-1#U@kz`@od_=+`riL#&T7)6?HG$V3OPe!+eqq4NWOj|Wfp zgtG@iwrW@<{(EMq*=?#+c=Dpl$yf71rIOFY{6EU2myTsCe2~+Aqx45jsJ@`Y=ai6d z$)P6Sg1m^98mk!Yb)F9Ik6+-?6F#M8xYs+HA#Q$u!aGlfbF9nRvx5?onq}kD4{#km zz+}Td&{$2SxS6jBTWXt`# z9yj0S^k(%0wlF`u!>7Z`vG+k+TF2f-xsFyA!Bbp{4NYGjdgOnS{-e%uuCe%`)SI&x zo%EdyWj!MKa%C%=MVOLx&*>DzIYneXc=zr;=MO1|+K`&R56sI4A0#j?OBsdZ%vc$??9!4&kv|+1x#YJjb^96S!aB{}i;FugD)CuHwD00&k(o=MrKCnJyJBLi zlXRy$?Wf0all%QaB4@9dxUh)X)TV#WduI~-mg~;$@}Ki(-oI1mR=)q{|MSJ?_ujvE z^<{vD2v@7qLUd|MyK9{o_fK`zwVVvE4oVh?&6;>DV!+SX+}V2S z%#^0%v&>tTKh4dUK2!HDT?a7f2>~_jI=B;K|bz)gxJw9>h)rnSPqv*_#Yr9H9Rg1PcZTtDYFL~Ok%EAM)C;r&t zc2bx-v*OjM_K;bfGLvh*v@ESsJj-}uv2Ev%88ZS4cI3tcb!0E|DwE&8@uutJ7{6!^ zH|LH!Ig2;%Thh3Ay6w5hBIAHB^O`JJI$CRV=LaiJxF+!bW$>o%43AG-o+*+m9_Xax zm)7mAer_(x{qt(!uTA`!9IoE79x2Dd(+~Ts_H%Ku5jr+gt4ON2Y(8VBx7DmON2gZ` z_GQX1%*f%}b?X05_hswXzPOm4RemmHqF#zgRK-6|ACDU4Nw*bPE@@bX?RHQ$;>x*L zt9JkNOgDG`3)-p^_WeI#8SDGa+lgaIYNHGLrmr1yC63S3Qkw8b&G8uHG!2d?(oC*L zd)}X&>E@;;DI%<~@BbM&ffME|ujFjk@v|_^64H=h`Z>qppa6%10z)GMOOt~D2NNR) zi-JG{h~)?tWD?+Da#Vl_GJsi)3IZ%l5IqV4EKLp!5IrnS4!zzBIQ?^%Sa#PfGMz8K z)HmAya^qp~6;Dzm=W6VAVegMyaQ1fn;wJsNg+KG(E_(Cly@CMCrl6>KF2-s3i%Vbk zUw&!)G^FsT+t#gH|9hq`?X9zqc62?ee!?i$?5g%j>k4AASFJY0vR)#R>Y{M;O=Z zSh!3-wfdE-Ot_ZTs+W`X>leDKi#sdCi*!7ndv(6-!c$t&drv=JvT9eGoxaVp+r@#& zq8TTD|6Z|*@o8a@u1eI#ZvrfzR2x6V-|(7#RCC$Fr-enXF)Pnj{dDJe!p)T0wKM(w zL3@$O$4_pYWOVAw(aIGYIlnyHUH$59*6HAB^(77UH)WYJLl>-BDgENXMDA554$Ig_ zs$JV42oe+*f1@ya?Xk~}5r1Y+UaoiXUF~z$=PQ$;+GX-x~8wHm?=l`|Vqv)u-34S9i@^{^4v8 zM+m!Ib=d#W;dfi@Md)Cu<-}3q2=eIlbIxbL8`}g3Yop}5+|N6|?(d%yu zly6d;FrD+r?l<#dYHPPY-Ew<Oe+Z*JQ-=8>Rc-Gdn8OC~vIWH<6?9 z!`v5Ewdzt#4_EeB*-m9_{16i{cOG}MLxs^+zw8!=3NtyThu1By{4V9=Xt~ec8g6WqQHUXR&bIArFblfn*oxxIaol+9GbWxOh}T)noSzc zb6hb$c*)o_e3PR>F1LwN^4hi0IbrcfS&XD~8~>zjkP`CNRhkI*-i@Llb6eiz{>JHe#nttpiuhj2zD>rP|t!xo|QI1Jez~lM- zZMiWW7TaVdZ}r<$zF^K*Qx4Z`CQs9oH}79+c(^`SIP#uO`m6OF4%^GBS8#HFIk7_Z za*IvMsf|twyj&(m$-GA;=g&JE+2$(cJXPp~|MG4Qq2tdS&D%;vg8vv!(@?#hg()(mo8l4*QiWdxYyK5pn2UH_})U$zIs=rtT1_St{1 zxcu0cv(XwxkE-orpPq?2Dfw#Z8@qL}9qlh?v*bQWT+Cd~r zv)=1>A3uYs!2H^Z_8*3GT2H9V+WfHR$(mayZnA3Lt&Q0o&~WEph2q+yQA#`u&+eAs z@!i#FUp_gNN$YTAf!ZA2^d~1y&5PVHXW@LmrA1R;xH-;fV1Hefw6I)?XJI+#E{0IS zyJ{;YD+^bfB^HIPI{n$dvqJeDTV4 zIY;T`)6ZEQdv2&1Ep!N+GygYlk(gtV8KYihWvr$6Gksx(Jxu9xaoXqpbFyAxMXTIb c1^;F34{fQf`ZPy^fq{X+)78&qol`;+0M453RsaA1 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..cdbf7e651208f9c2663ad9234daaff31aebf07bf GIT binary patch literal 6372 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 U$<0k=U|?YIboFyt=akR{0RE0>rT_o{ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4fa9bd445a6588cfe52d95ffa8e850791aff1fed GIT binary patch literal 3188 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000..e72ae0c --- /dev/null +++ b/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,43 @@ + + + Xem mã nguồn + Xem mã nguồn dự án trên GitLab + �ể sử dụng, hãy bật tất cả 3 tùy ch�n dưới, và thêm com.termux vào danh sách ứng dụng nhé ! + Chế độ b� qua + Bật tùy ch�n này để b� qua các ứng dụng đã ch�n thay vì ủy quy�n chúng + Danh sách ứng dụng + Nhập tên gói của một ứng dụng vào một dòng. Xem Cài đặt -> Ứng dụng để biết tên gói ứng dụng. + Kết nối khi khởi động + Máy chủ DNS + Cổng DNS (TCP) + Proxy cho ứng dụng + Lộ trình + Tất cả (Mặc định) + IP không phải của Trung Quốc + Nâng cao + Mật khẩu + Tên tài khoản + Tên ngư�i dùng & Xác thực mật khẩu + Xác thực + Proxy đang hoạt động + Kết nối + Failed to add profile %s + Lỗi xóa hồ sơ %s + IPv6 + Kích hoạt chuyển tiếp IPv6. Nếu máy chủ hỗ trợ IPv6, bạn có thể truy cập nội dung IPv6 từ mạng IPv4 khi bật tính năng này. + �ã kết nối với %s + �ã kích hoạt proxy SOCKS5 + global { perm_cache=1024; cache_dir={DIR}; server_port = 8091; server_ip = 0.0.0.0; query_method=tcp_only; min_ttl=15m; max_ttl=1w; timeout=10; daemon=on; pid_file={DIR}/pdnsd.pid; } server { label= upstream; ip = {IP}; port = {PORT}; uptest = none; } rr { name=localhost; reverse=on; a=127.0.0.1; owner=localhost; soa=localhost,root.localhost,42,86400,900,86400,86400; } + Thêm hồ sơ + Mặc định + Xóa hồ sơ + Xóa hồ sơ %s? + Hồ sơ + IP máy chủ + Cổng máy chủ + Bật/tắt proxy + Cổng UDP (Từ xa) + Chuyển tiếp UDP + Chuyển tiếp gói UDP tới badvpn-udpgw qua SOCKS5. �i�u này cần badvpn-udpgw chạy trên máy chủ từ xa (thư�ng listen trên 127.0.0.1) để hoạt động. + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..84b5665 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,12 @@ + + + + @string/adv_route_all + @string/adv_route_non_chn + + + + all + chn + + diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c1f65ba --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #5C6BC0 + \ No newline at end of file diff --git a/app/src/main/res/values/pdnsd.xml b/app/src/main/res/values/pdnsd.xml new file mode 100644 index 0000000..5053849 --- /dev/null +++ b/app/src/main/res/values/pdnsd.xml @@ -0,0 +1,32 @@ + + + +global { + perm_cache=1024; + cache_dir="{DIR}"; + server_port = 8091; + server_ip = 0.0.0.0; + query_method=tcp_only; + min_ttl=15m; + max_ttl=1w; + timeout=10; + daemon=on; + pid_file="{DIR}/pdnsd.pid"; +} + +server { + label= "upstream"; + ip = {IP}; + port = {PORT}; + uptest = none; +} + +rr { + name=localhost; + reverse=on; + a=127.0.0.1; + owner=localhost; + soa=localhost,root.localhost,42,86400,900,86400,86400; +} + + diff --git a/app/src/main/res/values/simpleroute.xml b/app/src/main/res/values/simpleroute.xml new file mode 100644 index 0000000..6fd7b6f --- /dev/null +++ b/app/src/main/res/values/simpleroute.xml @@ -0,0 +1,1154 @@ + + + + 1.0.0.0/15 + 1.2.0.0/16 + 1.4.0.0/14 + 1.9.0.0/16 + 1.10.0.0/15 + 1.16.0.0/13 + 1.32.0.0/13 + 1.40.0.0/14 + 1.44.0.0/16 + 1.46.0.0/15 + 1.52.0.0/14 + 1.64.0.0/14 + 1.72.0.0/13 + 1.96.0.0/12 + 1.112.0.0/14 + 1.120.0.0/13 + 1.128.0.0/11 + 1.160.0.0/12 + 1.176.0.0/14 + 1.186.0.0/15 + 1.200.0.0/15 + 1.208.0.0/12 + 1.224.0.0/11 + 2.0.0.0/7 + 4.0.0.0/6 + 8.0.0.0/7 + 11.0.0.0/8 + 12.0.0.0/7 + 14.0.0.0/12 + 14.32.0.0/11 + 14.64.0.0/11 + 14.96.0.0/14 + 14.100.0.0/15 + 14.102.0.0/16 + 14.128.0.0/15 + 14.132.0.0/15 + 14.136.0.0/13 + 14.160.0.0/11 + 14.192.0.0/14 + 14.198.0.0/15 + 14.200.0.0/14 + 14.206.0.0/15 + 14.224.0.0/11 + 15.0.0.0/8 + 16.0.0.0/5 + 24.0.0.0/7 + 26.0.0.0/8 + 27.0.0.0/13 + 27.32.0.0/14 + 27.48.0.0/12 + 27.64.0.0/11 + 27.96.0.0/14 + 27.100.0.0/15 + 27.102.0.0/16 + 27.104.0.0/13 + 27.112.0.0/12 + 27.130.0.0/15 + 27.132.0.0/14 + 27.136.0.0/13 + 27.145.0.0/16 + 27.146.0.0/15 + 27.160.0.0/12 + 27.176.0.0/13 + 27.228.0.0/14 + 27.232.0.0/13 + 27.240.0.0/12 + 28.0.0.0/6 + 32.0.0.0/6 + 36.0.0.0/16 + 36.2.0.0/15 + 36.8.0.0/13 + 36.37.0.0/16 + 36.38.0.0/15 + 36.50.0.0/16 + 36.52.0.0/14 + 36.64.0.0/11 + 36.224.0.0/12 + 36.240.0.0/13 + 36.252.0.0/15 + 36.255.0.0/16 + 37.0.0.0/8 + 38.0.0.0/8 + 39.0.0.0/10 + 39.96.0.0/11 + 39.192.0.0/10 + 40.0.0.0/7 + 42.0.0.0/14 + 42.8.0.0/13 + 42.16.0.0/12 + 42.32.0.0/12 + 42.60.0.0/15 + 42.62.0.0/16 + 42.64.0.0/12 + 42.82.0.0/15 + 42.96.0.0/16 + 42.98.0.0/15 + 42.104.0.0/13 + 42.112.0.0/13 + 42.123.0.0/16 + 42.124.0.0/14 + 42.144.0.0/13 + 42.152.0.0/14 + 42.156.0.0/16 + 42.187.0.0/16 + 42.188.0.0/14 + 42.200.0.0/15 + 42.241.0.0/16 + 43.0.0.0/8 + 44.0.0.0/6 + 48.0.0.0/8 + 49.0.0.0/14 + 49.8.0.0/13 + 49.16.0.0/12 + 49.32.0.0/12 + 49.48.0.0/15 + 49.50.0.0/16 + 49.56.0.0/13 + 49.96.0.0/12 + 49.124.0.0/14 + 49.128.0.0/13 + 49.136.0.0/14 + 49.142.0.0/15 + 49.144.0.0/13 + 49.156.0.0/14 + 49.160.0.0/11 + 49.192.0.0/12 + 49.212.0.0/14 + 49.216.0.0/14 + 49.224.0.0/13 + 49.236.0.0/14 + 49.240.0.0/12 + 50.0.0.0/7 + 52.0.0.0/7 + 54.0.0.0/9 + 54.128.0.0/10 + 54.192.0.0/12 + 54.208.0.0/13 + 54.216.0.0/14 + 54.220.0.0/15 + 54.224.0.0/11 + 55.0.0.0/8 + 56.0.0.0/7 + 58.0.0.0/13 + 58.8.0.0/14 + 58.12.0.0/15 + 58.26.0.0/15 + 58.28.0.0/15 + 58.64.0.0/15 + 58.68.0.0/14 + 58.72.0.0/13 + 58.80.0.0/15 + 58.82.0.0/16 + 58.84.0.0/14 + 58.88.0.0/13 + 58.96.0.0/14 + 58.102.0.0/15 + 58.104.0.0/13 + 58.112.0.0/14 + 58.120.0.0/13 + 58.136.0.0/13 + 58.145.0.0/16 + 58.146.0.0/15 + 58.148.0.0/14 + 58.152.0.0/15 + 58.156.0.0/14 + 58.160.0.0/11 + 58.224.0.0/12 + 59.0.0.0/11 + 59.84.0.0/14 + 59.88.0.0/13 + 59.96.0.0/13 + 59.104.0.0/15 + 59.106.0.0/16 + 59.112.0.0/12 + 59.128.0.0/12 + 59.144.0.0/13 + 59.152.0.0/15 + 59.154.0.0/16 + 59.156.0.0/14 + 59.160.0.0/13 + 59.168.0.0/14 + 59.176.0.0/12 + 60.32.0.0/12 + 60.48.0.0/14 + 60.52.0.0/15 + 60.54.0.0/16 + 60.56.0.0/14 + 60.60.0.0/15 + 60.62.0.0/16 + 60.64.0.0/10 + 60.128.0.0/11 + 60.192.0.0/15 + 60.196.0.0/14 + 60.224.0.0/13 + 60.234.0.0/16 + 60.236.0.0/14 + 60.240.0.0/14 + 60.244.0.0/15 + 60.246.0.0/16 + 60.248.0.0/14 + 60.253.0.0/16 + 60.254.0.0/16 + 61.0.0.0/11 + 61.32.0.0/12 + 61.56.0.0/13 + 61.64.0.0/10 + 61.192.0.0/11 + 61.224.0.0/13 + 61.238.0.0/15 + 61.244.0.0/14 + 61.248.0.0/13 + 62.0.0.0/7 + 64.0.0.0/3 + 96.0.0.0/6 + 100.0.0.0/10 + 100.128.0.0/9 + 101.0.0.0/14 + 101.8.0.0/13 + 101.50.0.0/15 + 101.53.0.0/16 + 101.55.0.0/16 + 101.56.0.0/13 + 101.78.0.0/15 + 101.96.0.0/13 + 101.108.0.0/14 + 101.112.0.0/13 + 101.127.0.0/16 + 101.128.0.0/16 + 101.136.0.0/13 + 101.160.0.0/11 + 101.202.0.0/15 + 101.208.0.0/12 + 101.234.0.0/15 + 101.250.0.0/15 + 101.255.0.0/16 + 102.0.0.0/7 + 104.0.0.0/7 + 106.0.0.0/15 + 106.10.0.0/16 + 106.51.0.0/16 + 106.64.0.0/13 + 106.72.0.0/15 + 106.76.0.0/14 + 106.96.0.0/13 + 106.104.0.0/14 + 106.128.0.0/10 + 106.192.0.0/11 + 106.240.0.0/12 + 107.0.0.0/8 + 108.0.0.0/7 + 110.0.0.0/14 + 110.4.0.0/15 + 110.8.0.0/13 + 110.20.0.0/14 + 110.24.0.0/13 + 110.32.0.0/13 + 110.44.0.0/14 + 110.49.0.0/16 + 110.50.0.0/16 + 110.54.0.0/15 + 110.66.0.0/15 + 110.68.0.0/14 + 110.74.0.0/16 + 110.76.0.0/14 + 110.92.0.0/15 + 110.128.0.0/12 + 110.144.0.0/13 + 110.158.0.0/15 + 110.160.0.0/14 + 110.164.0.0/15 + 110.168.0.0/13 + 110.224.0.0/14 + 110.232.0.0/14 + 110.238.0.0/15 + 111.64.0.0/15 + 111.67.0.0/16 + 111.68.0.0/14 + 111.80.0.0/14 + 111.84.0.0/16 + 111.86.0.0/15 + 111.88.0.0/13 + 111.96.0.0/12 + 111.118.0.0/15 + 111.125.0.0/16 + 111.168.0.0/15 + 111.171.0.0/16 + 111.184.0.0/15 + 111.188.0.0/14 + 111.216.0.0/14 + 111.220.0.0/15 + 111.223.0.0/16 + 111.232.0.0/13 + 111.240.0.0/12 + 112.68.0.0/14 + 112.72.0.0/16 + 112.76.0.0/14 + 112.104.0.0/14 + 112.108.0.0/15 + 112.110.0.0/16 + 112.118.0.0/15 + 112.120.0.0/15 + 112.133.0.0/16 + 112.134.0.0/15 + 112.136.0.0/13 + 112.144.0.0/12 + 112.160.0.0/11 + 112.196.0.0/14 + 112.200.0.0/13 + 112.208.0.0/12 + 113.10.0.0/15 + 113.19.0.0/16 + 113.20.0.0/14 + 113.28.0.0/15 + 113.30.0.0/16 + 113.32.0.0/13 + 113.40.0.0/14 + 113.52.0.0/15 + 113.59.0.0/16 + 113.60.0.0/15 + 113.130.0.0/15 + 113.144.0.0/12 + 113.160.0.0/11 + 113.192.0.0/15 + 113.196.0.0/14 + 113.203.0.0/16 + 113.208.0.0/16 + 113.210.0.0/15 + 113.212.0.0/15 + 113.216.0.0/15 + 113.252.0.0/14 + 114.0.0.0/12 + 114.16.0.0/13 + 114.24.0.0/14 + 114.29.0.0/16 + 114.30.0.0/15 + 114.32.0.0/12 + 114.48.0.0/14 + 114.52.0.0/15 + 114.56.0.0/14 + 114.69.0.0/16 + 114.70.0.0/15 + 114.72.0.0/13 + 114.108.0.0/14 + 114.120.0.0/13 + 114.128.0.0/14 + 114.133.0.0/16 + 114.134.0.0/16 + 114.136.0.0/15 + 114.140.0.0/14 + 114.144.0.0/12 + 114.160.0.0/11 + 114.192.0.0/14 + 114.198.0.0/15 + 114.200.0.0/13 + 115.0.0.0/12 + 115.16.0.0/13 + 115.30.0.0/15 + 115.36.0.0/14 + 115.40.0.0/14 + 115.64.0.0/11 + 115.96.0.0/14 + 115.108.0.0/14 + 115.112.0.0/13 + 115.124.0.0/14 + 115.128.0.0/12 + 115.144.0.0/14 + 115.160.0.0/13 + 115.176.0.0/14 + 115.184.0.0/14 + 115.188.0.0/15 + 115.240.0.0/12 + 116.0.0.0/16 + 116.12.0.0/16 + 116.14.0.0/15 + 116.32.0.0/12 + 116.48.0.0/14 + 116.58.0.0/15 + 116.64.0.0/14 + 116.68.0.0/16 + 116.70.0.0/15 + 116.72.0.0/14 + 116.80.0.0/14 + 116.84.0.0/16 + 116.86.0.0/15 + 116.88.0.0/14 + 116.92.0.0/15 + 116.94.0.0/16 + 116.96.0.0/12 + 116.118.0.0/15 + 116.120.0.0/13 + 116.193.0.0/16 + 116.197.0.0/16 + 116.199.0.0/16 + 116.200.0.0/14 + 116.206.0.0/16 + 116.212.0.0/15 + 116.214.0.0/16 + 116.220.0.0/14 + 116.240.0.0/15 + 116.250.0.0/15 + 116.254.0.0/15 + 117.0.0.0/13 + 117.16.0.0/14 + 117.20.0.0/16 + 117.46.0.0/15 + 117.52.0.0/14 + 117.56.0.0/16 + 117.58.0.0/16 + 117.74.0.0/16 + 117.96.0.0/14 + 117.102.0.0/15 + 117.104.0.0/15 + 117.108.0.0/14 + 117.120.0.0/14 + 117.192.0.0/10 + 118.0.0.0/12 + 118.16.0.0/13 + 118.27.0.0/16 + 118.32.0.0/11 + 118.67.0.0/16 + 118.68.0.0/14 + 118.82.0.0/15 + 118.86.0.0/15 + 118.88.0.0/16 + 118.90.0.0/15 + 118.92.0.0/14 + 118.96.0.0/12 + 118.127.0.0/16 + 118.128.0.0/14 + 118.136.0.0/13 + 118.148.0.0/14 + 118.152.0.0/13 + 118.160.0.0/12 + 118.176.0.0/15 + 118.179.0.0/16 + 118.185.0.0/16 + 118.189.0.0/16 + 118.200.0.0/15 + 118.208.0.0/14 + 118.214.0.0/15 + 118.216.0.0/13 + 118.231.0.0/16 + 118.232.0.0/14 + 118.236.0.0/15 + 118.238.0.0/16 + 118.240.0.0/15 + 118.243.0.0/16 + 119.2.0.0/16 + 119.9.0.0/16 + 119.10.0.0/15 + 119.12.0.0/14 + 119.17.0.0/16 + 119.18.0.0/16 + 119.24.0.0/14 + 119.30.0.0/15 + 119.40.0.0/16 + 119.42.0.0/15 + 119.46.0.0/15 + 119.56.0.0/16 + 119.59.0.0/16 + 119.63.0.0/16 + 119.64.0.0/13 + 119.72.0.0/14 + 119.76.0.0/15 + 119.81.0.0/16 + 119.82.0.0/15 + 119.92.0.0/14 + 119.104.0.0/14 + 119.110.0.0/15 + 119.148.0.0/14 + 119.152.0.0/13 + 119.160.0.0/15 + 119.168.0.0/13 + 119.192.0.0/11 + 119.224.0.0/13 + 119.234.0.0/15 + 119.236.0.0/14 + 119.240.0.0/13 + 119.252.0.0/16 + 120.16.0.0/13 + 120.28.0.0/15 + 120.50.0.0/15 + 120.56.0.0/13 + 120.72.0.0/14 + 120.88.0.0/15 + 120.93.0.0/16 + 120.96.0.0/11 + 120.136.0.0/13 + 120.144.0.0/12 + 120.160.0.0/11 + 121.0.0.0/14 + 121.6.0.0/15 + 121.44.0.0/15 + 121.46.0.0/16 + 121.50.0.0/16 + 121.52.0.0/14 + 121.58.0.0/16 + 121.64.0.0/14 + 121.72.0.0/14 + 121.78.0.0/15 + 121.80.0.0/13 + 121.88.0.0/16 + 121.90.0.0/15 + 121.92.0.0/14 + 121.96.0.0/11 + 121.128.0.0/10 + 121.200.0.0/16 + 121.202.0.0/15 + 121.208.0.0/12 + 121.240.0.0/13 + 121.252.0.0/15 + 121.254.0.0/16 + 122.0.0.0/14 + 122.11.0.0/16 + 122.15.0.0/16 + 122.16.0.0/12 + 122.32.0.0/12 + 122.49.0.0/16 + 122.50.0.0/16 + 122.52.0.0/14 + 122.56.0.0/13 + 122.98.0.0/15 + 122.100.0.0/14 + 122.104.0.0/13 + 122.116.0.0/15 + 122.118.0.0/16 + 122.120.0.0/13 + 122.128.0.0/13 + 122.144.0.0/13 + 122.152.0.0/14 + 122.160.0.0/12 + 122.176.0.0/13 + 122.184.0.0/14 + 122.196.0.0/15 + 122.199.0.0/16 + 122.200.0.0/14 + 122.208.0.0/12 + 122.248.0.0/13 + 123.0.0.0/14 + 123.16.0.0/12 + 123.32.0.0/12 + 123.48.0.0/14 + 123.63.0.0/16 + 123.98.0.0/15 + 123.100.0.0/16 + 123.102.0.0/15 + 123.104.0.0/13 + 123.136.0.0/16 + 123.140.0.0/14 + 123.176.0.0/16 + 123.192.0.0/14 + 123.198.0.0/15 + 123.200.0.0/14 + 123.204.0.0/15 + 123.208.0.0/12 + 123.224.0.0/13 + 123.236.0.0/14 + 123.240.0.0/14 + 123.248.0.0/16 + 123.250.0.0/15 + 123.252.0.0/16 + 123.254.0.0/15 + 124.0.0.0/13 + 124.8.0.0/14 + 124.12.0.0/15 + 124.18.0.0/15 + 124.24.0.0/14 + 124.28.0.0/15 + 124.30.0.0/16 + 124.32.0.0/13 + 124.40.0.0/15 + 124.43.0.0/16 + 124.44.0.0/14 + 124.48.0.0/12 + 124.66.0.0/16 + 124.80.0.0/13 + 124.96.0.0/12 + 124.120.0.0/14 + 124.124.0.0/15 + 124.136.0.0/13 + 124.144.0.0/14 + 124.148.0.0/15 + 124.150.0.0/16 + 124.153.0.0/16 + 124.154.0.0/15 + 124.157.0.0/16 + 124.158.0.0/15 + 124.168.0.0/14 + 124.176.0.0/12 + 124.194.0.0/15 + 124.197.0.0/16 + 124.198.0.0/15 + 124.208.0.0/13 + 124.216.0.0/14 + 124.240.0.0/15 + 124.243.0.0/16 + 124.244.0.0/14 + 124.248.0.0/16 + 124.252.0.0/14 + 125.0.0.0/11 + 125.48.0.0/12 + 125.99.0.0/16 + 125.100.0.0/14 + 125.128.0.0/11 + 125.160.0.0/13 + 125.168.0.0/16 + 125.170.0.0/16 + 125.172.0.0/14 + 125.176.0.0/12 + 125.192.0.0/12 + 125.208.0.0/15 + 125.212.0.0/14 + 125.224.0.0/11 + 126.0.0.0/8 + 128.0.0.0/6 + 132.0.0.0/7 + 134.0.0.0/9 + 134.128.0.0/10 + 134.192.0.0/14 + 134.197.0.0/16 + 134.198.0.0/15 + 134.200.0.0/13 + 134.208.0.0/12 + 134.224.0.0/11 + 135.0.0.0/8 + 136.0.0.0/7 + 138.0.0.0/8 + 139.0.0.0/13 + 139.8.0.0/16 + 139.10.0.0/15 + 139.12.0.0/14 + 139.16.0.0/12 + 139.32.0.0/11 + 139.64.0.0/10 + 139.128.0.0/16 + 139.130.0.0/15 + 139.132.0.0/14 + 139.136.0.0/13 + 139.144.0.0/14 + 139.149.0.0/16 + 139.150.0.0/15 + 139.152.0.0/15 + 139.154.0.0/16 + 139.156.0.0/15 + 139.158.0.0/16 + 139.160.0.0/13 + 139.168.0.0/15 + 139.171.0.0/16 + 139.172.0.0/14 + 139.177.0.0/16 + 139.178.0.0/15 + 139.180.0.0/15 + 139.182.0.0/16 + 139.184.0.0/15 + 139.187.0.0/16 + 139.188.0.0/16 + 139.190.0.0/15 + 139.192.0.0/14 + 139.216.0.0/14 + 139.222.0.0/15 + 139.225.0.0/16 + 139.228.0.0/14 + 139.232.0.0/13 + 139.240.0.0/12 + 140.0.0.0/10 + 140.64.0.0/13 + 140.72.0.0/15 + 140.74.0.0/16 + 140.76.0.0/14 + 140.80.0.0/12 + 140.96.0.0/11 + 140.128.0.0/13 + 140.136.0.0/14 + 140.140.0.0/15 + 140.142.0.0/16 + 140.144.0.0/12 + 140.160.0.0/11 + 140.192.0.0/13 + 140.200.0.0/14 + 140.204.0.0/16 + 140.208.0.0/15 + 140.211.0.0/16 + 140.212.0.0/14 + 140.216.0.0/13 + 140.225.0.0/16 + 140.226.0.0/15 + 140.228.0.0/14 + 140.232.0.0/14 + 140.236.0.0/16 + 140.238.0.0/15 + 140.241.0.0/16 + 140.242.0.0/16 + 140.244.0.0/15 + 140.247.0.0/16 + 140.248.0.0/16 + 140.251.0.0/16 + 140.252.0.0/15 + 140.254.0.0/16 + 141.0.0.0/8 + 142.0.0.0/7 + 144.1.0.0/16 + 144.2.0.0/15 + 144.4.0.0/15 + 144.6.0.0/16 + 144.8.0.0/14 + 144.13.0.0/16 + 144.14.0.0/15 + 144.16.0.0/12 + 144.32.0.0/12 + 144.48.0.0/14 + 144.53.0.0/16 + 144.54.0.0/15 + 144.56.0.0/13 + 144.64.0.0/11 + 144.96.0.0/12 + 144.112.0.0/13 + 144.120.0.0/15 + 144.122.0.0/16 + 144.124.0.0/14 + 144.128.0.0/10 + 144.192.0.0/11 + 144.224.0.0/12 + 144.240.0.0/13 + 144.248.0.0/14 + 144.252.0.0/15 + 144.254.0.0/16 + 145.0.0.0/8 + 146.0.0.0/7 + 148.0.0.0/7 + 150.1.0.0/16 + 150.2.0.0/15 + 150.4.0.0/14 + 150.8.0.0/13 + 150.16.0.0/12 + 150.32.0.0/11 + 150.64.0.0/11 + 150.96.0.0/12 + 150.112.0.0/15 + 150.114.0.0/16 + 150.116.0.0/14 + 150.120.0.0/16 + 150.123.0.0/16 + 150.124.0.0/14 + 150.128.0.0/13 + 150.136.0.0/15 + 150.140.0.0/14 + 150.144.0.0/12 + 150.160.0.0/11 + 150.192.0.0/12 + 150.208.0.0/13 + 150.216.0.0/14 + 150.220.0.0/15 + 150.222.0.0/16 + 150.224.0.0/12 + 150.240.0.0/13 + 150.248.0.0/14 + 150.252.0.0/15 + 150.254.0.0/16 + 151.0.0.0/8 + 152.0.0.0/8 + 153.1.0.0/16 + 153.2.0.0/16 + 153.4.0.0/14 + 153.8.0.0/13 + 153.16.0.0/12 + 153.32.0.0/15 + 153.38.0.0/15 + 153.40.0.0/13 + 153.48.0.0/12 + 153.64.0.0/11 + 153.96.0.0/15 + 153.98.0.0/16 + 153.100.0.0/16 + 153.102.0.0/15 + 153.104.0.0/13 + 153.112.0.0/14 + 153.116.0.0/15 + 153.120.0.0/13 + 153.128.0.0/9 + 154.0.0.0/7 + 156.0.0.0/8 + 157.1.0.0/16 + 157.2.0.0/15 + 157.4.0.0/14 + 157.8.0.0/13 + 157.16.0.0/15 + 157.19.0.0/16 + 157.20.0.0/14 + 157.24.0.0/13 + 157.32.0.0/12 + 157.48.0.0/13 + 157.56.0.0/14 + 157.60.0.0/16 + 157.62.0.0/15 + 157.64.0.0/11 + 157.96.0.0/12 + 157.112.0.0/13 + 157.120.0.0/15 + 157.123.0.0/16 + 157.124.0.0/14 + 157.128.0.0/12 + 157.144.0.0/14 + 157.149.0.0/16 + 157.150.0.0/15 + 157.152.0.0/14 + 157.157.0.0/16 + 157.158.0.0/15 + 157.160.0.0/11 + 157.192.0.0/11 + 157.224.0.0/12 + 157.240.0.0/13 + 157.248.0.0/14 + 157.252.0.0/15 + 157.254.0.0/16 + 158.0.0.0/8 + 159.0.0.0/9 + 159.128.0.0/10 + 159.192.0.0/11 + 159.224.0.0/15 + 159.227.0.0/16 + 159.228.0.0/14 + 159.232.0.0/13 + 159.240.0.0/12 + 160.0.0.0/8 + 161.0.0.0/9 + 161.128.0.0/10 + 161.192.0.0/13 + 161.200.0.0/14 + 161.204.0.0/15 + 161.206.0.0/16 + 161.208.0.0/12 + 161.224.0.0/11 + 162.0.0.0/10 + 162.64.0.0/11 + 162.96.0.0/13 + 162.104.0.0/16 + 162.106.0.0/15 + 162.108.0.0/14 + 162.112.0.0/12 + 162.128.0.0/9 + 163.1.0.0/16 + 163.2.0.0/15 + 163.4.0.0/14 + 163.8.0.0/13 + 163.16.0.0/12 + 163.32.0.0/11 + 163.64.0.0/11 + 163.96.0.0/12 + 163.112.0.0/13 + 163.120.0.0/14 + 163.124.0.0/16 + 163.126.0.0/15 + 163.128.0.0/13 + 163.136.0.0/14 + 163.140.0.0/15 + 163.143.0.0/16 + 163.144.0.0/12 + 163.160.0.0/12 + 163.176.0.0/16 + 163.178.0.0/16 + 163.180.0.0/14 + 163.184.0.0/13 + 163.192.0.0/13 + 163.200.0.0/14 + 163.205.0.0/16 + 163.206.0.0/15 + 163.208.0.0/12 + 163.224.0.0/11 + 164.0.0.0/7 + 166.0.0.0/10 + 166.64.0.0/11 + 166.96.0.0/13 + 166.104.0.0/14 + 166.108.0.0/15 + 166.110.0.0/16 + 166.112.0.0/12 + 166.128.0.0/9 + 167.0.0.0/9 + 167.128.0.0/13 + 167.136.0.0/15 + 167.138.0.0/16 + 167.140.0.0/14 + 167.144.0.0/12 + 167.160.0.0/11 + 167.192.0.0/10 + 168.0.0.0/9 + 168.128.0.0/11 + 168.161.0.0/16 + 168.162.0.0/15 + 168.164.0.0/14 + 168.168.0.0/13 + 168.176.0.0/12 + 168.192.0.0/10 + 169.0.0.0/9 + 169.128.0.0/10 + 169.192.0.0/11 + 169.224.0.0/12 + 169.240.0.0/13 + 169.248.0.0/14 + 169.252.0.0/15 + 169.255.0.0/16 + 170.0.0.0/8 + 171.0.0.0/13 + 171.16.0.0/12 + 171.32.0.0/15 + 171.48.0.0/12 + 171.64.0.0/12 + 171.96.0.0/13 + 171.128.0.0/10 + 171.192.0.0/12 + 171.224.0.0/11 + 172.0.0.0/12 + 172.32.0.0/11 + 172.64.0.0/10 + 172.128.0.0/9 + 173.0.0.0/8 + 174.0.0.0/8 + 175.28.0.0/15 + 175.32.0.0/13 + 175.40.0.0/15 + 175.45.0.0/16 + 175.96.0.0/14 + 175.100.0.0/15 + 175.103.0.0/16 + 175.104.0.0/13 + 175.112.0.0/12 + 175.128.0.0/12 + 175.144.0.0/15 + 175.156.0.0/14 + 175.176.0.0/15 + 175.179.0.0/16 + 175.180.0.0/14 + 175.184.0.0/16 + 175.192.0.0/10 + 176.0.0.0/6 + 180.0.0.0/10 + 180.64.0.0/13 + 180.72.0.0/14 + 180.80.0.0/14 + 180.87.0.0/16 + 180.92.0.0/14 + 180.128.0.0/15 + 180.131.0.0/16 + 180.132.0.0/14 + 180.144.0.0/13 + 180.176.0.0/13 + 180.188.0.0/14 + 180.192.0.0/13 + 180.200.0.0/16 + 180.204.0.0/14 + 180.210.0.0/15 + 180.214.0.0/15 + 180.216.0.0/14 + 180.220.0.0/15 + 180.222.0.0/16 + 180.224.0.0/11 + 181.0.0.0/8 + 182.0.0.0/11 + 182.48.0.0/16 + 182.50.0.0/16 + 182.52.0.0/14 + 182.56.0.0/14 + 182.60.0.0/16 + 182.62.0.0/15 + 182.64.0.0/12 + 182.93.0.0/16 + 182.94.0.0/15 + 182.152.0.0/14 + 182.156.0.0/16 + 182.158.0.0/15 + 182.160.0.0/13 + 182.168.0.0/14 + 182.172.0.0/15 + 182.176.0.0/12 + 182.192.0.0/13 + 182.208.0.0/12 + 182.224.0.0/13 + 182.232.0.0/14 + 182.236.0.0/15 + 182.239.0.0/16 + 182.248.0.0/14 + 182.252.0.0/15 + 182.255.0.0/16 + 183.72.0.0/13 + 183.80.0.0/14 + 183.86.0.0/15 + 183.88.0.0/14 + 183.96.0.0/11 + 183.171.0.0/16 + 183.176.0.0/13 + 184.0.0.0/5 + 192.0.0.0/9 + 192.128.0.0/11 + 192.160.0.0/13 + 192.169.0.0/16 + 192.170.0.0/15 + 192.172.0.0/14 + 192.176.0.0/12 + 192.192.0.0/10 + 193.0.0.0/8 + 194.0.0.0/7 + 196.0.0.0/7 + 198.0.0.0/12 + 198.16.0.0/15 + 198.20.0.0/14 + 198.24.0.0/13 + 198.32.0.0/11 + 198.64.0.0/10 + 198.128.0.0/9 + 199.0.0.0/8 + 200.0.0.0/7 + 202.0.0.0/10 + 202.64.0.0/11 + 202.122.0.0/15 + 202.124.0.0/14 + 202.128.0.0/10 + 202.208.0.0/12 + 202.224.0.0/11 + 203.0.0.0/10 + 203.64.0.0/12 + 203.80.0.0/13 + 203.88.0.0/14 + 203.92.0.0/16 + 203.94.0.0/15 + 203.96.0.0/11 + 203.128.0.0/9 + 204.0.0.0/6 + 208.0.0.0/7 + 210.0.0.0/13 + 210.8.0.0/14 + 210.14.0.0/15 + 210.16.0.0/14 + 210.20.0.0/16 + 210.23.0.0/16 + 210.24.0.0/16 + 210.48.0.0/15 + 210.50.0.0/16 + 210.54.0.0/15 + 210.56.0.0/13 + 210.64.0.0/13 + 210.79.0.0/16 + 210.80.0.0/15 + 210.84.0.0/14 + 210.88.0.0/13 + 210.96.0.0/11 + 210.128.0.0/9 + 211.0.0.0/10 + 211.72.0.0/13 + 211.104.0.0/13 + 211.112.0.0/12 + 211.128.0.0/13 + 211.168.0.0/13 + 211.176.0.0/12 + 211.192.0.0/10 + 212.0.0.0/6 + 216.0.0.0/7 + 218.32.0.0/12 + 218.48.0.0/13 + 218.100.0.0/14 + 218.110.0.0/15 + 218.112.0.0/12 + 218.128.0.0/10 + 218.208.0.0/12 + 218.224.0.0/12 + 218.248.0.0/16 + 218.250.0.0/15 + 218.252.0.0/14 + 219.0.0.0/10 + 219.64.0.0/13 + 219.73.0.0/16 + 219.74.0.0/15 + 219.76.0.0/14 + 219.80.0.0/15 + 219.83.0.0/16 + 219.84.0.0/14 + 219.88.0.0/13 + 219.96.0.0/11 + 219.160.0.0/11 + 219.192.0.0/12 + 219.208.0.0/13 + 219.240.0.0/15 + 219.248.0.0/13 + 220.0.0.0/10 + 220.64.0.0/11 + 220.96.0.0/12 + 220.116.0.0/14 + 220.120.0.0/13 + 220.128.0.0/12 + 220.144.0.0/13 + 220.152.0.0/15 + 220.156.0.0/14 + 220.208.0.0/12 + 220.224.0.0/13 + 220.232.0.0/15 + 220.235.0.0/16 + 220.236.0.0/14 + 220.240.0.0/15 + 220.244.0.0/14 + 220.253.0.0/16 + 220.254.0.0/15 + 221.12.0.0/16 + 221.16.0.0/12 + 221.32.0.0/11 + 221.64.0.0/11 + 221.96.0.0/12 + 221.112.0.0/13 + 221.120.0.0/15 + 221.124.0.0/14 + 221.128.0.0/16 + 221.132.0.0/14 + 221.138.0.0/15 + 221.140.0.0/14 + 221.144.0.0/12 + 221.160.0.0/13 + 221.168.0.0/14 + 221.184.0.0/13 + 221.199.0.0/16 + 221.240.0.0/12 + 222.0.0.0/12 + 222.96.0.0/12 + 222.112.0.0/13 + 222.120.0.0/14 + 222.124.0.0/16 + 222.126.0.0/15 + 222.144.0.0/12 + 222.164.0.0/14 + 222.224.0.0/12 + 222.250.0.0/15 + 222.252.0.0/14 + 223.16.0.0/14 + 223.22.0.0/15 + 223.24.0.0/13 + 223.32.0.0/11 + 223.118.0.0/15 + 223.130.0.0/15 + 223.132.0.0/14 + 223.136.0.0/13 + 223.164.0.0/15 + 223.168.0.0/13 + 223.176.0.0/12 + 223.194.0.0/15 + 223.196.0.0/15 + 223.200.0.0/16 + 223.204.0.0/14 + 223.216.0.0/14 + 223.222.0.0/15 + 223.224.0.0/12 + 223.252.0.0/15 + 223.255.0.0/16 + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..532e66c --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,49 @@ + + + + SocksDroid + In order to use, enable all available option below, and add com.termux to App List ! + View source code + View project source code on GitLab + Profile + Default + + Connection + Server IP + Server Port + IPv6 + Enable IPv6 forwarding. If the server supports IPv6, you can access IPv6 contents from IPv4 network with this enabled. + UDP Forwarding + Forward UDP packets to badvpn-udpgw via SOCKS5. This needs badvpn-udpgw running on the remote server (normally listening on 127.0.0.1) to work. + UDP Gateway (Remote) + + Authentication + Username & Password Authentication + Username + Password + + Advanced + Route + All (Default) + Non-Chinese IPs + DNS Server + DNS Port (TCP) + Connect on Boot + Per-app Proxy + Bypass Mode + Enable this option to bypass selected apps instead of proxying them + App List + Enter one app\'s package name in one line. See Settings -> Apps for package names of apps. + + Add Profile + Delete Profile + Delete profile %s? + Switch on / off the proxy + + Failed to add profile %s + Error deleting profile %s + SOCKS5 proxy activated + Connected to %s + Proxy active + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..4acce86 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/xml/backup_descriptor.xml b/app/src/main/res/xml/backup_descriptor.xml new file mode 100644 index 0000000..a608293 --- /dev/null +++ b/app/src/main/res/xml/backup_descriptor.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..fe7a40d --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml new file mode 100644 index 0000000..3cd9baf --- /dev/null +++ b/app/src/main/res/xml/settings.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..476beb1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f82049a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

    Y{ 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Ď>lQ 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..d3db109 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'